[FFmpeg-cvslog] Merge commit '551c6775abb5e0ad34c26d7e23bc6fbbe8ccc9d4'

Derek Buitenhuis git at videolan.org
Thu Apr 14 14:56:01 CEST 2016


ffmpeg | branch: master | Derek Buitenhuis <derek.buitenhuis at gmail.com> | Thu Apr 14 13:49:55 2016 +0100| [28abb216cbd5736d65973165b830c08815ce0227] | committer: Derek Buitenhuis

Merge commit '551c6775abb5e0ad34c26d7e23bc6fbbe8ccc9d4'

* commit '551c6775abb5e0ad34c26d7e23bc6fbbe8ccc9d4':
  lavu: VAAPI hwcontext implementation

Merged-by: Derek Buitenhuis <derek.buitenhuis at gmail.com>

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=28abb216cbd5736d65973165b830c08815ce0227
---

 configure                      |    4 +
 doc/APIchanges                 |    3 +
 libavutil/Makefile             |    3 +
 libavutil/hwcontext.c          |    3 +
 libavutil/hwcontext.h          |    1 +
 libavutil/hwcontext_internal.h |    1 +
 libavutil/hwcontext_vaapi.c    |  850 ++++++++++++++++++++++++++++++++++++++++
 libavutil/hwcontext_vaapi.h    |   82 ++++
 libavutil/version.h            |    2 +-
 9 files changed, 948 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 0ff1b9d..f981a4e 100755
--- a/configure
+++ b/configure
@@ -5852,6 +5852,10 @@ enabled vaapi &&
     check_lib va/va.h vaInitialize -lva ||
     disable vaapi
 
+enabled vaapi &&
+    check_code cc "va/va.h" "vaCreateSurfaces(0, 0, 0, 0, 0, 0, 0, 0)" ||
+    disable vaapi
+
 enabled vaapi && enabled xlib &&
     check_lib2 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 &&
     enable vaapi_x11
diff --git a/doc/APIchanges b/doc/APIchanges
index 626fe18..7972b07 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -15,6 +15,9 @@ libavutil:     2015-08-28
 
 API changes, most recent first:
 
+2016-xx-xx - xxxxxxx - lavu 55.22.0 - hwcontext_vaapi.h
+  Add new installed header with VAAPI-specific hwcontext definitions.
+
 2016-xx-xx - xxxxxxx - lavu 55.21.0 - hwcontext.h
   Add AVHWFramesConstraints and associated API.
 
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 7a3076f..a91e701 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -33,6 +33,7 @@ HEADERS = adler32.h                                                     \
           hmac.h                                                        \
           hwcontext.h                                                   \
           hwcontext_cuda.h                                              \
+          hwcontext_vaapi.h                                             \
           hwcontext_vdpau.h                                             \
           imgutils.h                                                    \
           intfloat.h                                                    \
@@ -153,6 +154,7 @@ OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o                     \
 OBJS-$(CONFIG_LZO)                      += lzo.o
 OBJS-$(CONFIG_OPENCL)                   += opencl.o opencl_internal.o
 OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
+OBJS-$(CONFIG_VAAPI)                    += hwcontext_vaapi.o
 OBJS-$(CONFIG_VDPAU)                    += hwcontext_vdpau.o
 
 OBJS += $(COMPAT_OBJS:%=../compat/%)
@@ -161,6 +163,7 @@ OBJS += $(COMPAT_OBJS:%=../compat/%)
 SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
 
 SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda.h
+SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
 SKIPHEADERS-$(CONFIG_VDPAU)            += hwcontext_vdpau.h
 SKIPHEADERS-$(HAVE_ATOMICS_GCC)        += atomic_gcc.h
 SKIPHEADERS-$(HAVE_ATOMICS_SUNCC)      += atomic_suncc.h
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index 4dfec2c..b34b317 100644
--- a/libavutil/hwcontext.c
+++ b/libavutil/hwcontext.c
@@ -32,6 +32,9 @@ static const HWContextType *hw_table[] = {
 #if CONFIG_CUDA
     &ff_hwcontext_type_cuda,
 #endif
+#if CONFIG_VAAPI
+    &ff_hwcontext_type_vaapi,
+#endif
 #if CONFIG_VDPAU
     &ff_hwcontext_type_vdpau,
 #endif
diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
index ee0f1df..44be197 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 0ea04f8..f18b616 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -97,6 +97,7 @@ struct AVHWFramesInternal {
 };
 
 extern const HWContextType ff_hwcontext_type_cuda;
+extern const HWContextType ff_hwcontext_type_vaapi;
 extern const HWContextType ff_hwcontext_type_vdpau;
 
 #endif /* AVUTIL_HWCONTEXT_INTERNAL_H */
diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
new file mode 100644
index 0000000..c2cdaa9
--- /dev/null
+++ b/libavutil/hwcontext_vaapi.c
@@ -0,0 +1,850 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; 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 "hwcontext.h"
+#include "hwcontext_internal.h"
+#include "hwcontext_vaapi.h"
+#include "mem.h"
+#include "pixdesc.h"
+#include "pixfmt.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 *formats;
+    int              nb_formats;
+} VAAPIDeviceContext;
+
+typedef struct VAAPIFramesContext {
+    // Surface attributes set at create time.
+    VASurfaceAttrib *attributes;
+    int           nb_attributes;
+    // 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 {
+    // The source hardware frame of this mapping (with hw_frames_ctx set).
+    const AVFrame *source;
+    // VAAPI_MAP_* flags which apply to this mapping.
+    int flags;
+    // Handle to the derived or copied image which is mapped.
+    VAImage image;
+} VAAPISurfaceMap;
+
+#define MAP(va, rt, av) { \
+        VA_FOURCC_ ## va, \
+        VA_RT_FORMAT_ ## rt, \
+        AV_PIX_FMT_ ## av \
+    }
+// The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
+// plane swap cases.  The frame handling below tries to hide these.
+static struct {
+    unsigned int fourcc;
+    unsigned int rt_format;
+    enum AVPixelFormat pix_fmt;
+} vaapi_format_map[] = {
+    MAP(NV12, YUV420,  NV12),
+    MAP(YV12, YUV420,  YUV420P), // With U/V planes swapped.
+    MAP(IYUV, YUV420,  YUV420P),
+  //MAP(I420, YUV420,  YUV420P), // Not in libva but used by Intel driver.
+#ifdef VA_FOURCC_YV16
+    MAP(YV16, YUV422,  YUV422P), // With U/V planes swapped.
+#endif
+    MAP(422H, YUV422,  YUV422P),
+    MAP(UYVY, YUV422,  UYVY422),
+    MAP(YUY2, YUV422,  YUYV422),
+    MAP(Y800, YUV400,  GRAY8),
+#ifdef VA_FOURCC_P010
+  //MAP(P010, YUV420_10BPP, P010),
+#endif
+    MAP(BGRA, RGB32,   BGRA),
+  //MAP(BGRX, RGB32,   BGR0),
+    MAP(RGBA, RGB32,   RGBA),
+  //MAP(RGBX, RGB32,   RGB0),
+    MAP(ABGR, RGB32,   ABGR),
+  //MAP(XBGR, RGB32,   0BGR),
+    MAP(ARGB, RGB32,   ARGB),
+  //MAP(XRGB, RGB32,   0RGB),
+};
+#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 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->nb_formats; i++) {
+        if (ctx->formats[i].pix_fmt == pix_fmt) {
+            *image_format = &ctx->formats[i].image_format;
+            return 0;
+        }
+    }
+    return AVERROR(EINVAL);
+}
+
+static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
+                                        const void *hwconfig,
+                                        AVHWFramesConstraints *constraints)
+{
+    AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
+    const AVVAAPIHWConfig *config = hwconfig;
+    AVVAAPIHWConfig *tmp_config;
+    VASurfaceAttrib *attr_list = NULL;
+    VAStatus vas;
+    enum AVPixelFormat pix_fmt;
+    unsigned int fourcc;
+    int err, i, j, attr_count, pix_fmt_count;
+
+    if (!hwconfig) {
+        // No configuration was provided, so we create a temporary pipeline
+        // configuration in order to query all supported image formats.
+
+        tmp_config = av_mallocz(sizeof(*config));
+        if (!tmp_config)
+            return AVERROR(ENOMEM);
+
+        vas = vaCreateConfig(hwctx->display,
+                             VAProfileNone, VAEntrypointVideoProc,
+                             NULL, 0, &tmp_config->config_id);
+        if (vas != VA_STATUS_SUCCESS) {
+            // No vpp.  We might still be able to do something useful if
+            // codecs are supported, so try to make the most-commonly
+            // supported decoder configuration we can to query instead.
+            vas = vaCreateConfig(hwctx->display,
+                                 VAProfileH264ConstrainedBaseline,
+                                 VAEntrypointVLD, NULL, 0,
+                                 &tmp_config->config_id);
+            if (vas != VA_STATUS_SUCCESS) {
+                av_freep(&tmp_config);
+                return AVERROR(ENOSYS);
+            }
+        }
+
+        config = tmp_config;
+    }
+
+    attr_count = 0;
+    vas = vaQuerySurfaceAttributes(hwctx->display, config->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(*attr_list));
+    if (!attr_list) {
+        err = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    vas = vaQuerySurfaceAttributes(hwctx->display, config->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++) {
+        switch (attr_list[i].type) {
+        case VASurfaceAttribPixelFormat:
+            fourcc = attr_list[i].value.value.i;
+            pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+            if (pix_fmt != AV_PIX_FMT_NONE) {
+                ++pix_fmt_count;
+            } else {
+                // Something unsupported - ignore.
+            }
+            break;
+        case VASurfaceAttribMinWidth:
+            constraints->min_width  = attr_list[i].value.value.i;
+            break;
+        case VASurfaceAttribMinHeight:
+            constraints->min_height = attr_list[i].value.value.i;
+            break;
+        case VASurfaceAttribMaxWidth:
+            constraints->max_width  = attr_list[i].value.value.i;
+            break;
+        case VASurfaceAttribMaxHeight:
+            constraints->max_height = attr_list[i].value.value.i;
+            break;
+        }
+    }
+    if (pix_fmt_count == 0) {
+        // Nothing usable found.  Presumably there exists something which
+        // works, so leave the set null to indicate unknown.
+        constraints->valid_sw_formats = NULL;
+    } else {
+        constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
+                                                        sizeof(pix_fmt));
+        if (!constraints->valid_sw_formats) {
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        for (i = j = 0; i < attr_count; i++) {
+            if (attr_list[i].type != VASurfaceAttribPixelFormat)
+                continue;
+            fourcc = attr_list[i].value.value.i;
+            pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+            if (pix_fmt != AV_PIX_FMT_NONE)
+                constraints->valid_sw_formats[j++] = pix_fmt;
+        }
+        av_assert0(j == pix_fmt_count);
+        constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
+    }
+
+    constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
+    if (!constraints->valid_hw_formats) {
+        err = AVERROR(ENOMEM);
+        goto fail;
+    }
+    constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
+    constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
+
+    err = 0;
+fail:
+    av_freep(&attr_list);
+    if (!hwconfig) {
+        vaDestroyConfig(hwctx->display, tmp_config->config_id);
+        av_freep(&tmp_config);
+    }
+    return err;
+}
+
+static int vaapi_device_init(AVHWDeviceContext *hwdev)
+{
+    VAAPIDeviceContext *ctx = hwdev->internal->priv;
+    AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
+    AVHWFramesConstraints *constraints = NULL;
+    VAImageFormat *image_list = NULL;
+    VAStatus vas;
+    int err, i, j, image_count;
+    enum AVPixelFormat pix_fmt;
+    unsigned int fourcc;
+
+    constraints = av_mallocz(sizeof(*constraints));
+    if (!constraints)
+        goto fail;
+
+    err = vaapi_frames_get_constraints(hwdev, NULL, constraints);
+    if (err < 0)
+        goto fail;
+
+    image_count = vaMaxNumImageFormats(hwctx->display);
+    if (image_count <= 0) {
+        err = AVERROR(EIO);
+        goto fail;
+    }
+    image_list = av_malloc(image_count * sizeof(*image_list));
+    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->formats  = av_malloc(image_count * sizeof(*ctx->formats));
+    if (!ctx->formats) {
+        err = AVERROR(ENOMEM);
+        goto fail;
+    }
+    ctx->nb_formats = 0;
+    for (i = 0; i < image_count; i++) {
+        fourcc  = image_list[i].fourcc;
+        pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+        for (j = 0; constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE; j++) {
+            if (pix_fmt == constraints->valid_sw_formats[j])
+                break;
+        }
+        if (constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE) {
+            av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
+                   fourcc, av_get_pix_fmt_name(pix_fmt));
+            ctx->formats[ctx->nb_formats].pix_fmt      = pix_fmt;
+            ctx->formats[ctx->nb_formats].image_format = image_list[i];
+            ++ctx->nb_formats;
+        } else {
+            av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n", fourcc);
+        }
+    }
+
+    av_free(image_list);
+    av_hwframe_constraints_free(&constraints);
+    return 0;
+fail:
+    av_freep(&ctx->formats);
+    av_free(image_list);
+    av_hwframe_constraints_free(&constraints);
+    return err;
+}
+
+static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
+{
+    VAAPIDeviceContext *ctx = hwdev->internal->priv;
+
+    av_freep(&ctx->formats);
+}
+
+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 destroy 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->nb_attributes);
+    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) {
+        // This is a fixed-size pool, so we must still be in the initial
+        // allocation sequence.
+        av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
+        avfc->surface_ids[avfc->nb_surfaces] = surface_id;
+        ++avfc->nb_surfaces;
+    }
+
+    return ref;
+}
+
+static int vaapi_frames_init(AVHWFramesContext *hwfc)
+{
+    AVVAAPIFramesContext  *avfc = hwfc->hwctx;
+    VAAPIFramesContext     *ctx = hwfc->internal->priv;
+    AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAImageFormat *expected_format;
+    AVBufferRef *test_surface = NULL;
+    VASurfaceID test_surface_id;
+    VAImage test_image;
+    VAStatus vas;
+    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) {
+        int need_memory_type = 1, need_pixel_format = 1;
+        for (i = 0; i < avfc->nb_attributes; i++) {
+            if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
+                need_memory_type  = 0;
+            if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
+                need_pixel_format = 0;
+        }
+        ctx->nb_attributes =
+            avfc->nb_attributes + need_memory_type + need_pixel_format;
+
+        ctx->attributes = av_malloc(ctx->nb_attributes *
+                                        sizeof(*ctx->attributes));
+        if (!ctx->attributes) {
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        for (i = 0; i < avfc->nb_attributes; i++)
+            ctx->attributes[i] = avfc->attributes[i];
+        if (need_memory_type) {
+            ctx->attributes[i++] = (VASurfaceAttrib) {
+                .type          = VASurfaceAttribMemoryType,
+                .flags         = VA_SURFACE_ATTRIB_SETTABLE,
+                .value.type    = VAGenericValueTypeInteger,
+                .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
+            };
+        }
+        if (need_pixel_format) {
+            ctx->attributes[i++] = (VASurfaceAttrib) {
+                .type          = VASurfaceAttribPixelFormat,
+                .flags         = VA_SURFACE_ATTRIB_SETTABLE,
+                .value.type    = VAGenericValueTypeInteger,
+                .value.value.i = fourcc,
+            };
+        }
+        av_assert0(i == ctx->nb_attributes);
+
+        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->nb_surfaces = 0;
+            avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
+                                          sizeof(*avfc->surface_ids));
+            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->nb_surfaces = 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;
+        }
+    }
+
+    // Allocate a single surface to test whether vaDeriveImage() is going
+    // to work for the specific configuration.
+    if (hwfc->pool) {
+        test_surface = av_buffer_pool_get(hwfc->pool);
+        if (!test_surface) {
+            av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
+                   "user-configured buffer pool.\n");
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
+    } else {
+        test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
+        if (!test_surface) {
+            av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
+                   "internal buffer pool.\n");
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
+    }
+    test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
+
+    ctx->derive_works = 0;
+
+    err = vaapi_get_image_format(hwfc->device_ctx,
+                                 hwfc->sw_format, &expected_format);
+    if (err == 0) {
+        vas = vaDeriveImage(hwctx->display, test_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");
+    }
+
+    av_buffer_unref(&test_surface);
+    return 0;
+
+fail:
+    av_buffer_unref(&test_surface);
+    av_freep(&avfc->surface_ids);
+    av_freep(&ctx->attributes);
+    return err;
+}
+
+static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
+{
+    AVVAAPIFramesContext *avfc = hwfc->hwctx;
+    VAAPIFramesContext    *ctx = hwfc->internal->priv;
+
+    av_freep(&avfc->surface_ids);
+    av_freep(&ctx->attributes);
+}
+
+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;
+    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->nb_formats + 1) * sizeof(*pix_fmts));
+    if (!pix_fmts)
+        return AVERROR(ENOMEM);
+
+    pix_fmts[0] = preferred_format;
+    k = 1;
+    for (i = 0; i < ctx->nb_formats; i++) {
+        if (ctx->formats[i].pix_fmt == preferred_format)
+            continue;
+        av_assert0(k < ctx->nb_formats);
+        pix_fmts[k++] = ctx->formats[i].pix_fmt;
+    }
+    av_assert0(k == ctx->nb_formats);
+    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 = NULL;
+    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;
+    }
+
+    // The memory which we map using derive need not be connected to the CPU
+    // in a way conducive to fast access.  On Gen7-Gen9 Intel graphics, the
+    // memory is mappable but not cached, so normal memcpy()-like access is
+    // very slow to read it (but writing is ok).  It is possible to read much
+    // faster with a copy routine which is aware of the limitation, but we
+    // assume for now that the user is not aware of that and would therefore
+    // prefer not to be given direct-mapped memory if they request read access.
+    if (ctx->derive_works &&
+        ((flags & VAAPI_MAP_DIRECT) || !(flags & VAAPI_MAP_READ))) {
+        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 (
+#ifdef VA_FOURCC_YV16
+        map->image.format.fourcc == VA_FOURCC_YV16 ||
+#endif
+        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:
+    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 = src->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:
+    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),
+    .device_hwconfig_size   = sizeof(AVVAAPIHWConfig),
+    .frames_hwctx_size      = sizeof(AVVAAPIFramesContext),
+    .frames_priv_size       = sizeof(VAAPIFramesContext),
+
+    .device_init            = &vaapi_device_init,
+    .device_uninit          = &vaapi_device_uninit,
+    .frames_get_constraints = &vaapi_frames_get_constraints,
+    .frames_init            = &vaapi_frames_init,
+    .frames_uninit          = &vaapi_frames_uninit,
+    .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,
+        AV_PIX_FMT_NONE
+    },
+};
diff --git a/libavutil/hwcontext_vaapi.h b/libavutil/hwcontext_vaapi.h
new file mode 100644
index 0000000..7fd1a36
--- /dev/null
+++ b/libavutil/hwcontext_vaapi.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; 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>
+
+/**
+ * @file
+ * API-specific header for AV_HWDEVICE_TYPE_VAAPI.
+ *
+ * Dynamic frame pools are supported, but note that any pool used as a render
+ * target is required to be of fixed size in order to be be usable as an
+ * argument to vaCreateContext().
+ *
+ * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
+ * with the data pointer set to a VASurfaceID.
+ */
+
+/**
+ * VAAPI connection details.
+ *
+ * Allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVVAAPIDeviceContext {
+    /**
+     * The VADisplay handle, to be filled by the user.
+     */
+    VADisplay display;
+} AVVAAPIDeviceContext;
+
+/**
+ * VAAPI-specific data associated with a frame pool.
+ *
+ * Allocated as AVHWFramesContext.hwctx.
+ */
+typedef struct AVVAAPIFramesContext {
+    /**
+     * Set by the user to apply surface attributes to all surfaces in
+     * the frame pool.  If null, default settings are used.
+     */
+    VASurfaceAttrib *attributes;
+    int           nb_attributes;
+    /**
+     * The surfaces IDs of all surfaces in the pool after creation.
+     * Only valid if AVHWFramesContext.initial_pool_size was positive.
+     * These are intended to be used as the render_targets arguments to
+     * vaCreateContext().
+     */
+    VASurfaceID     *surface_ids;
+    int           nb_surfaces;
+} AVVAAPIFramesContext;
+
+/**
+ * VAAPI hardware pipeline configuration details.
+ *
+ * Allocated with av_hwdevice_hwconfig_alloc().
+ */
+typedef struct AVVAAPIHWConfig {
+    /**
+     * ID of a VAAPI pipeline configuration.
+     */
+    VAConfigID config_id;
+} AVVAAPIHWConfig;
+
+#endif /* AVUTIL_HWCONTEXT_VAAPI_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 7d8fdb3..cf7458e 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -64,7 +64,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  55
-#define LIBAVUTIL_VERSION_MINOR  21
+#define LIBAVUTIL_VERSION_MINOR  22
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \


======================================================================

diff --cc configure
index 0ff1b9d,5376e67..f981a4e
--- a/configure
+++ b/configure
@@@ -5836,22 -4681,10 +5836,26 @@@ if enabled x11grab; the
      require Xfixes X11/extensions/Xfixes.h XFixesGetCursorImage -lXfixes
  fi
  
 +check_func_headers "windows.h" CreateDIBSection "$gdigrab_indev_extralibs"
 +
 +enabled dxva2api_h &&
 +    check_cc <<EOF && enable dxva2api_cobj
 +#define _WIN32_WINNT 0x0600
 +#define COBJMACROS
 +#include <windows.h>
 +#include <d3d9.h>
 +#include <dxva2api.h>
 +int main(void) { IDirectXVideoDecoder *o = NULL; IDirectXVideoDecoder_Release(o); return 0; }
 +EOF
 +
 +enabled vaapi &&
 +    check_lib va/va.h vaInitialize -lva ||
 +    disable vaapi
 +
+ enabled vaapi &&
+     check_code cc "va/va.h" "vaCreateSurfaces(0, 0, 0, 0, 0, 0, 0, 0)" ||
+     disable vaapi
+ 
  enabled vaapi && enabled xlib &&
      check_lib2 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 &&
      enable vaapi_x11
diff --cc doc/APIchanges
index 626fe18,bce8504..7972b07
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@@ -15,111 -13,60 +15,114 @@@ libavutil:     2015-08-2
  
  API changes, most recent first:
  
 -2016-xx-xx - xxxxxxx - lavu 55.9.0 - hwcontext_vaapi.h
++2016-xx-xx - xxxxxxx - lavu 55.22.0 - hwcontext_vaapi.h
+   Add new installed header with VAAPI-specific hwcontext definitions.
+ 
 -2016-xx-xx - xxxxxxx - lavu 55.8.0 - pixfmt.h
 -  Deprecate all AV_PIX_FMT_VAAPI_* formats.
 -  Replaced by AV_PIX_FMT_VAAPI.
 -
 -2016-xx-xx - xxxxxxx - lavu 55.7.0 - hwcontext.h
 +2016-xx-xx - xxxxxxx - lavu 55.21.0 - hwcontext.h
    Add AVHWFramesConstraints and associated API.
  
 -2016-02-23 - 9200514 - lavf 57.5.0 - avformat.h
 +2016-04-11 - xxxxxxx - lavf 57.33.0 - avformat.h
    Add AVStream.codecpar, deprecate AVStream.codec.
  
 -2016-xx-xx - lavc 57.14.0 - avcodec.h
 -  998e1b8 - Add AVCodecParameters and its related API.
 -  a806834 - Add av_get_audio_frame_duration2().
 +2016-04-02 - xxxxxxx - lavu 55.20.100 - base64.h
 +  Add AV_BASE64_DECODE_SIZE(x) macro.
 +
 +2016-xx-xx - lavc 57.33.0 - avcodec.h
 +  xxxxxxx - Add AVCodecParameters and its related API.
 +  xxxxxxx - Add av_get_audio_frame_duration2().
 +
 +2016-03-11 - xxxxxxx - lavf/lavc 57.28.101
 +  Add requirement to bitstream filtering API that returned packets with
 +  size == 0 and side_data_elems == 0 are to be skipped by the caller.
 +
 +2016-XX-XX - xxxxxxx - lavf 57.28.100
 +  Add protocol blacklisting API
 +
 +2016-02-28 - xxxxxxx - lavc 57.27.101
 +  Validate AVFrame returned by get_buffer2 to have required
 +  planes not NULL and unused planes set to NULL as crashes
 +  and buffer overflow are possible with certain streams if
 +  that is not the case.
 +
 +2016-xx-xx - xxxxxxx - lavc 57.27.100 - avcodec.h
 +  "flags2" decoding option now allows the flag "ass_ro_flush_noop" preventing
 +  the reset of the ASS ReadOrder field on flush. This affects the content of
 +  AVSubtitles.rects[N]->ass when "sub_text_format" is set to "ass" (see
 +  previous entry).
 +
 +2016-xx-xx - xxxxxxx - lavc 57.26.100 - avcodec.h
 +  Add a "sub_text_format" subtitles decoding option allowing the values "ass"
 +  (recommended) and "ass_with_timings" (not recommended, deprecated, default).
 +  The default value for this option will change to "ass" at the next major
 +  libavcodec version bump.
 +
 +  The current default is "ass_with_timings" for compatibility. This means that
 +  all subtitles text decoders currently still output ASS with timings printed
 +  as strings in the AVSubtitles.rects[N]->ass fields.
 +
 +  Setting "sub_text_format" to "ass" allows a better timing accuracy (ASS
 +  timing is limited to a 1/100 time base, so this is relevant for any subtitles
 +  format needing a bigger one), ease timing adjustments, and prevents the need
 +  of removing the timing from the decoded string yourself. This form is also
 +  known as "the Matroska form". The timing information (start time, duration)
 +  can be found in the AVSubtitles fields.
 +
 +2016-xx-xx - lavc 57.25.0 - avcodec.h
 +  Add AVCodecContext.hw_frames_ctx.
  
 -2016-02-22 - ec4c483 - lavf 57.4.0 - avformat.h
 -  Add AVFormatContext.protocol_whitelist and protocol_blacklist.
 -  Add 'protocol_whitelist' and 'protocol_blacklist' private options for
 -  avio_open2().
 +2016-xx-xx - lavfi 6.36.0 - avfilter.h
 +  xxxxxxx avfilter.h - Add AVFilterLink.hw_frames_ctx.
 +  xxxxxxx buffersrc.h - Add AVBufferSrcParameters and functions for handling it.
  
 -2016-02-14 - 7b3214d0 - lavc 57.13.0 - avcodec.h
 -  Add AVCodecContext.hw_frames_ctx.
 +2016-02-xx - xxxxxxx - lavc 57.25.100
 +  Add AV_PKT_DATA_MPEGTS_STREAM_ID for exporting the MPEGTS stream ID.
  
 -2016-02-14 - lavfi 6.2.0 - avfilter.h
 -  b3dd30d avfilter.h - Add AVFilterLink.hw_frames_ctx.
 -          buffersrc.h - Add AVBufferSrcParameters and functions for handling it.
 +2016-xx-xx - lavu 55.18.100
 +  xxxxxxx audio_fifo.h - Add av_audio_fifo_peek_at().
  
 -2016-02-14 - lavu 55.6.0
 -  721a4ef buffer.h - Add av_buffer_pool_init2().
 -  89923e4 hwcontext.h - Add a new installed header hwcontext.h with a new API
 +2016-xx-xx - lavu 55.18.0
 +  xxxxxxx buffer.h - Add av_buffer_pool_init2().
 +  xxxxxxx hwcontext.h - Add a new installed header hwcontext.h with a new API
                          for handling hwaccel frames.
 -  ad884d1 hwcontext_cuda.h - Add a new installed header hwcontext_cuda.h with
 +  xxxxxxx hwcontext_cuda.h - Add a new installed header hwcontext_cuda.h with
                               CUDA-specific hwcontext definitions.
 -  a001ce3 hwcontext_vdpau.h - Add a new installed header hwcontext_vdpau.h with
 +  xxxxxxx hwcontext_vdpau.h - Add a new installed header hwcontext_vdpau.h with
                                VDPAU-specific hwcontext definitions.
 -  7bc780c pixfmt.h - Add AV_PIX_FMT_CUDA.
 +  xxxxxxx pixfmt.h - Add AV_PIX_FMT_CUDA.
  
 -2016-01-24 - 9f61abc - lavf 57.3.0 - avformat.h
 +-------- 8< --------- FFmpeg 3.0 was cut here -------- 8< ---------
 +
 +2016-02-10 - bc9a596 / 9f61abc - lavf 57.25.100 / 57.3.0 - avformat.h
    Add AVFormatContext.opaque, io_open and io_close, allowing custom IO
 -  for muxers and demuxers that open additional files.
  
 -2015-12-12 - 2c68113 - lavc 57.12.0 - avcodec.h
 +2016-02-01 - 1dba837 - lavf 57.24.100 - avformat.h, avio.h
 +  Add protocol_whitelist to AVFormatContext, AVIOContext
 +
 +2016-01-31 - 66e9d2f - lavu 55.17.100 - frame.h
 +  Add AV_FRAME_DATA_GOP_TIMECODE for exporting MPEG1/2 GOP timecodes.
 +
 +2016-01-01 - 5e8b053 / 2c68113 - lavc 57.21.100 / 57.12.0 - avcodec.h
    Add AVCodecDescriptor.profiles and avcodec_profile_name().
  
 -2015-12-06 - lavc 57.11.0 - avcodec.h dirac.h
 -  31c51f7 - Add av_packet_add_side_data().
 -  84adab3 - Add AVCodecContext.coded_side_data.
 -  f0b769c - Add AVCPBProperties API.
 -  e02de9d - Add a new public header dirac.h containing
 +2015-12-28 - 1f9139b - lavf 57.21.100 - avformat.h
 +  Add automatic bitstream filtering; add av_apply_bitstream_filters()
 +
 +2015-12-22 - 39a09e9 - lavfi 6.21.101 - avfilter.h
 +  Deprecate avfilter_link_set_closed().
 +  Applications are not supposed to mess with links,
 +  they should close the sinks.
 +
 +2015-12-17 - lavc 57.18.100 / 57.11.0 - avcodec.h dirac.h
 +  xxxxxxx - Add av_packet_add_side_data().
 +  xxxxxxx - Add AVCodecContext.coded_side_data.
 +  xxxxxxx - Add AVCPBProperties API.
 +  xxxxxxx - Add a new public header dirac.h containing
              av_dirac_parse_sequence_header()
  
 -2015-11-20 - 462a54e - lavc 57.9.1 - avcodec.h
 +2015-12-11 - 676a93f - lavf 57.20.100 - avformat.h
 +  Add av_program_add_stream_index()
 +
 +2015-11-29 - 93fb4a4 - lavc 57.16.101 - avcodec.h
    Deprecate rtp_callback without replacement, i.e. it won't be possible to
    get image slices before the full frame is encoded any more. The libavformat
    rtpenc muxer can still be used for RFC-2190 packetization.
diff --cc libavutil/Makefile
index 7a3076f,0956703..a91e701
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@@ -128,39 -96,26 +129,41 @@@ OBJS = adler32.
         opt.o                                                            \
         parseutils.o                                                     \
         pixdesc.o                                                        \
 +       pixelutils.o                                                     \
         random_seed.o                                                    \
         rational.o                                                       \
 +       reverse.o                                                        \
         rc4.o                                                            \
 +       ripemd.o                                                         \
         samplefmt.o                                                      \
         sha.o                                                            \
 +       sha512.o                                                         \
         stereo3d.o                                                       \
 +       threadmessage.o                                                  \
         time.o                                                           \
 +       timecode.o                                                       \
         tree.o                                                           \
 +       twofish.o                                                        \
         utils.o                                                          \
 +       xga_font_data.o                                                  \
         xtea.o                                                           \
 +       tea.o                                                            \
 +
 +OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o                     \
  
  OBJS-$(CONFIG_LZO)                      += lzo.o
 +OBJS-$(CONFIG_OPENCL)                   += opencl.o opencl_internal.o
  OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
+ OBJS-$(CONFIG_VAAPI)                    += hwcontext_vaapi.o
  OBJS-$(CONFIG_VDPAU)                    += hwcontext_vdpau.o
  
  OBJS += $(COMPAT_OBJS:%=../compat/%)
  
 +# Windows resource file
 +SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
 +
  SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda.h
+ SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
  SKIPHEADERS-$(CONFIG_VDPAU)            += hwcontext_vdpau.h
  SKIPHEADERS-$(HAVE_ATOMICS_GCC)        += atomic_gcc.h
  SKIPHEADERS-$(HAVE_ATOMICS_SUNCC)      += atomic_suncc.h
diff --cc libavutil/hwcontext_vaapi.c
index 0000000,1a385ba..c2cdaa9
mode 000000,100644..100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@@ -1,0 -1,850 +1,850 @@@
+ /*
 - * This file is part of Libav.
++ * This file is part of FFmpeg.
+  *
 - * Libav is free software; you can redistribute it and/or
++ * FFmpeg 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,
++ * FFmpeg 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
++ * License along with FFmpeg; 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 "hwcontext.h"
+ #include "hwcontext_internal.h"
+ #include "hwcontext_vaapi.h"
+ #include "mem.h"
+ #include "pixdesc.h"
+ #include "pixfmt.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 *formats;
+     int              nb_formats;
+ } VAAPIDeviceContext;
+ 
+ typedef struct VAAPIFramesContext {
+     // Surface attributes set at create time.
+     VASurfaceAttrib *attributes;
+     int           nb_attributes;
+     // 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 {
+     // The source hardware frame of this mapping (with hw_frames_ctx set).
+     const AVFrame *source;
+     // VAAPI_MAP_* flags which apply to this mapping.
+     int flags;
+     // Handle to the derived or copied image which is mapped.
+     VAImage image;
+ } VAAPISurfaceMap;
+ 
+ #define MAP(va, rt, av) { \
+         VA_FOURCC_ ## va, \
+         VA_RT_FORMAT_ ## rt, \
+         AV_PIX_FMT_ ## av \
+     }
+ // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
+ // plane swap cases.  The frame handling below tries to hide these.
+ static struct {
+     unsigned int fourcc;
+     unsigned int rt_format;
+     enum AVPixelFormat pix_fmt;
+ } vaapi_format_map[] = {
+     MAP(NV12, YUV420,  NV12),
+     MAP(YV12, YUV420,  YUV420P), // With U/V planes swapped.
+     MAP(IYUV, YUV420,  YUV420P),
+   //MAP(I420, YUV420,  YUV420P), // Not in libva but used by Intel driver.
+ #ifdef VA_FOURCC_YV16
+     MAP(YV16, YUV422,  YUV422P), // With U/V planes swapped.
+ #endif
+     MAP(422H, YUV422,  YUV422P),
+     MAP(UYVY, YUV422,  UYVY422),
+     MAP(YUY2, YUV422,  YUYV422),
+     MAP(Y800, YUV400,  GRAY8),
+ #ifdef VA_FOURCC_P010
+   //MAP(P010, YUV420_10BPP, P010),
+ #endif
+     MAP(BGRA, RGB32,   BGRA),
+   //MAP(BGRX, RGB32,   BGR0),
+     MAP(RGBA, RGB32,   RGBA),
+   //MAP(RGBX, RGB32,   RGB0),
+     MAP(ABGR, RGB32,   ABGR),
+   //MAP(XBGR, RGB32,   0BGR),
+     MAP(ARGB, RGB32,   ARGB),
+   //MAP(XRGB, RGB32,   0RGB),
+ };
+ #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 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->nb_formats; i++) {
+         if (ctx->formats[i].pix_fmt == pix_fmt) {
+             *image_format = &ctx->formats[i].image_format;
+             return 0;
+         }
+     }
+     return AVERROR(EINVAL);
+ }
+ 
+ static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
+                                         const void *hwconfig,
+                                         AVHWFramesConstraints *constraints)
+ {
+     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
+     const AVVAAPIHWConfig *config = hwconfig;
+     AVVAAPIHWConfig *tmp_config;
+     VASurfaceAttrib *attr_list = NULL;
+     VAStatus vas;
+     enum AVPixelFormat pix_fmt;
+     unsigned int fourcc;
+     int err, i, j, attr_count, pix_fmt_count;
+ 
+     if (!hwconfig) {
+         // No configuration was provided, so we create a temporary pipeline
+         // configuration in order to query all supported image formats.
+ 
+         tmp_config = av_mallocz(sizeof(*config));
+         if (!tmp_config)
+             return AVERROR(ENOMEM);
+ 
+         vas = vaCreateConfig(hwctx->display,
+                              VAProfileNone, VAEntrypointVideoProc,
+                              NULL, 0, &tmp_config->config_id);
+         if (vas != VA_STATUS_SUCCESS) {
+             // No vpp.  We might still be able to do something useful if
+             // codecs are supported, so try to make the most-commonly
+             // supported decoder configuration we can to query instead.
+             vas = vaCreateConfig(hwctx->display,
+                                  VAProfileH264ConstrainedBaseline,
+                                  VAEntrypointVLD, NULL, 0,
+                                  &tmp_config->config_id);
+             if (vas != VA_STATUS_SUCCESS) {
+                 av_freep(&tmp_config);
+                 return AVERROR(ENOSYS);
+             }
+         }
+ 
+         config = tmp_config;
+     }
+ 
+     attr_count = 0;
+     vas = vaQuerySurfaceAttributes(hwctx->display, config->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(*attr_list));
+     if (!attr_list) {
+         err = AVERROR(ENOMEM);
+         goto fail;
+     }
+ 
+     vas = vaQuerySurfaceAttributes(hwctx->display, config->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++) {
+         switch (attr_list[i].type) {
+         case VASurfaceAttribPixelFormat:
+             fourcc = attr_list[i].value.value.i;
+             pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+             if (pix_fmt != AV_PIX_FMT_NONE) {
+                 ++pix_fmt_count;
+             } else {
+                 // Something unsupported - ignore.
+             }
+             break;
+         case VASurfaceAttribMinWidth:
+             constraints->min_width  = attr_list[i].value.value.i;
+             break;
+         case VASurfaceAttribMinHeight:
+             constraints->min_height = attr_list[i].value.value.i;
+             break;
+         case VASurfaceAttribMaxWidth:
+             constraints->max_width  = attr_list[i].value.value.i;
+             break;
+         case VASurfaceAttribMaxHeight:
+             constraints->max_height = attr_list[i].value.value.i;
+             break;
+         }
+     }
+     if (pix_fmt_count == 0) {
+         // Nothing usable found.  Presumably there exists something which
+         // works, so leave the set null to indicate unknown.
+         constraints->valid_sw_formats = NULL;
+     } else {
+         constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
+                                                         sizeof(pix_fmt));
+         if (!constraints->valid_sw_formats) {
+             err = AVERROR(ENOMEM);
+             goto fail;
+         }
+ 
+         for (i = j = 0; i < attr_count; i++) {
+             if (attr_list[i].type != VASurfaceAttribPixelFormat)
+                 continue;
+             fourcc = attr_list[i].value.value.i;
+             pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+             if (pix_fmt != AV_PIX_FMT_NONE)
+                 constraints->valid_sw_formats[j++] = pix_fmt;
+         }
+         av_assert0(j == pix_fmt_count);
+         constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
+     }
+ 
+     constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
+     if (!constraints->valid_hw_formats) {
+         err = AVERROR(ENOMEM);
+         goto fail;
+     }
+     constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
+     constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
+ 
+     err = 0;
+ fail:
+     av_freep(&attr_list);
+     if (!hwconfig) {
+         vaDestroyConfig(hwctx->display, tmp_config->config_id);
+         av_freep(&tmp_config);
+     }
+     return err;
+ }
+ 
+ static int vaapi_device_init(AVHWDeviceContext *hwdev)
+ {
+     VAAPIDeviceContext *ctx = hwdev->internal->priv;
+     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
+     AVHWFramesConstraints *constraints = NULL;
+     VAImageFormat *image_list = NULL;
+     VAStatus vas;
+     int err, i, j, image_count;
+     enum AVPixelFormat pix_fmt;
+     unsigned int fourcc;
+ 
+     constraints = av_mallocz(sizeof(*constraints));
+     if (!constraints)
+         goto fail;
+ 
+     err = vaapi_frames_get_constraints(hwdev, NULL, constraints);
+     if (err < 0)
+         goto fail;
+ 
+     image_count = vaMaxNumImageFormats(hwctx->display);
+     if (image_count <= 0) {
+         err = AVERROR(EIO);
+         goto fail;
+     }
+     image_list = av_malloc(image_count * sizeof(*image_list));
+     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->formats  = av_malloc(image_count * sizeof(*ctx->formats));
+     if (!ctx->formats) {
+         err = AVERROR(ENOMEM);
+         goto fail;
+     }
+     ctx->nb_formats = 0;
+     for (i = 0; i < image_count; i++) {
+         fourcc  = image_list[i].fourcc;
+         pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+         for (j = 0; constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE; j++) {
+             if (pix_fmt == constraints->valid_sw_formats[j])
+                 break;
+         }
+         if (constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE) {
+             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
+                    fourcc, av_get_pix_fmt_name(pix_fmt));
+             ctx->formats[ctx->nb_formats].pix_fmt      = pix_fmt;
+             ctx->formats[ctx->nb_formats].image_format = image_list[i];
+             ++ctx->nb_formats;
+         } else {
+             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n", fourcc);
+         }
+     }
+ 
+     av_free(image_list);
+     av_hwframe_constraints_free(&constraints);
+     return 0;
+ fail:
+     av_freep(&ctx->formats);
+     av_free(image_list);
+     av_hwframe_constraints_free(&constraints);
+     return err;
+ }
+ 
+ static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
+ {
+     VAAPIDeviceContext *ctx = hwdev->internal->priv;
+ 
+     av_freep(&ctx->formats);
+ }
+ 
+ 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 destroy 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->nb_attributes);
+     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) {
+         // This is a fixed-size pool, so we must still be in the initial
+         // allocation sequence.
+         av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
+         avfc->surface_ids[avfc->nb_surfaces] = surface_id;
+         ++avfc->nb_surfaces;
+     }
+ 
+     return ref;
+ }
+ 
+ static int vaapi_frames_init(AVHWFramesContext *hwfc)
+ {
+     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
+     VAAPIFramesContext     *ctx = hwfc->internal->priv;
+     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+     VAImageFormat *expected_format;
+     AVBufferRef *test_surface = NULL;
+     VASurfaceID test_surface_id;
+     VAImage test_image;
+     VAStatus vas;
+     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) {
+         int need_memory_type = 1, need_pixel_format = 1;
+         for (i = 0; i < avfc->nb_attributes; i++) {
+             if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
+                 need_memory_type  = 0;
+             if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
+                 need_pixel_format = 0;
+         }
+         ctx->nb_attributes =
+             avfc->nb_attributes + need_memory_type + need_pixel_format;
+ 
+         ctx->attributes = av_malloc(ctx->nb_attributes *
+                                         sizeof(*ctx->attributes));
+         if (!ctx->attributes) {
+             err = AVERROR(ENOMEM);
+             goto fail;
+         }
+ 
+         for (i = 0; i < avfc->nb_attributes; i++)
+             ctx->attributes[i] = avfc->attributes[i];
+         if (need_memory_type) {
+             ctx->attributes[i++] = (VASurfaceAttrib) {
+                 .type          = VASurfaceAttribMemoryType,
+                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
+                 .value.type    = VAGenericValueTypeInteger,
+                 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
+             };
+         }
+         if (need_pixel_format) {
+             ctx->attributes[i++] = (VASurfaceAttrib) {
+                 .type          = VASurfaceAttribPixelFormat,
+                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
+                 .value.type    = VAGenericValueTypeInteger,
+                 .value.value.i = fourcc,
+             };
+         }
+         av_assert0(i == ctx->nb_attributes);
+ 
+         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->nb_surfaces = 0;
+             avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
+                                           sizeof(*avfc->surface_ids));
+             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->nb_surfaces = 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;
+         }
+     }
+ 
+     // Allocate a single surface to test whether vaDeriveImage() is going
+     // to work for the specific configuration.
+     if (hwfc->pool) {
+         test_surface = av_buffer_pool_get(hwfc->pool);
+         if (!test_surface) {
+             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
+                    "user-configured buffer pool.\n");
+             err = AVERROR(ENOMEM);
+             goto fail;
+         }
+     } else {
+         test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
+         if (!test_surface) {
+             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
+                    "internal buffer pool.\n");
+             err = AVERROR(ENOMEM);
+             goto fail;
+         }
+     }
+     test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
+ 
+     ctx->derive_works = 0;
+ 
+     err = vaapi_get_image_format(hwfc->device_ctx,
+                                  hwfc->sw_format, &expected_format);
+     if (err == 0) {
+         vas = vaDeriveImage(hwctx->display, test_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");
+     }
+ 
+     av_buffer_unref(&test_surface);
+     return 0;
+ 
+ fail:
+     av_buffer_unref(&test_surface);
+     av_freep(&avfc->surface_ids);
+     av_freep(&ctx->attributes);
+     return err;
+ }
+ 
+ static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
+ {
+     AVVAAPIFramesContext *avfc = hwfc->hwctx;
+     VAAPIFramesContext    *ctx = hwfc->internal->priv;
+ 
+     av_freep(&avfc->surface_ids);
+     av_freep(&ctx->attributes);
+ }
+ 
+ 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;
+     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->nb_formats + 1) * sizeof(*pix_fmts));
+     if (!pix_fmts)
+         return AVERROR(ENOMEM);
+ 
+     pix_fmts[0] = preferred_format;
+     k = 1;
+     for (i = 0; i < ctx->nb_formats; i++) {
+         if (ctx->formats[i].pix_fmt == preferred_format)
+             continue;
+         av_assert0(k < ctx->nb_formats);
+         pix_fmts[k++] = ctx->formats[i].pix_fmt;
+     }
+     av_assert0(k == ctx->nb_formats);
+     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 = NULL;
+     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;
+     }
+ 
+     // The memory which we map using derive need not be connected to the CPU
+     // in a way conducive to fast access.  On Gen7-Gen9 Intel graphics, the
+     // memory is mappable but not cached, so normal memcpy()-like access is
+     // very slow to read it (but writing is ok).  It is possible to read much
+     // faster with a copy routine which is aware of the limitation, but we
+     // assume for now that the user is not aware of that and would therefore
+     // prefer not to be given direct-mapped memory if they request read access.
+     if (ctx->derive_works &&
+         ((flags & VAAPI_MAP_DIRECT) || !(flags & VAAPI_MAP_READ))) {
+         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 (
+ #ifdef VA_FOURCC_YV16
+         map->image.format.fourcc == VA_FOURCC_YV16 ||
+ #endif
+         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:
+     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 = src->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:
+     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),
+     .device_hwconfig_size   = sizeof(AVVAAPIHWConfig),
+     .frames_hwctx_size      = sizeof(AVVAAPIFramesContext),
+     .frames_priv_size       = sizeof(VAAPIFramesContext),
+ 
+     .device_init            = &vaapi_device_init,
+     .device_uninit          = &vaapi_device_uninit,
+     .frames_get_constraints = &vaapi_frames_get_constraints,
+     .frames_init            = &vaapi_frames_init,
+     .frames_uninit          = &vaapi_frames_uninit,
+     .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,
+         AV_PIX_FMT_NONE
+     },
+ };
diff --cc libavutil/hwcontext_vaapi.h
index 0000000,1c87f5d..7fd1a36
mode 000000,100644..100644
--- a/libavutil/hwcontext_vaapi.h
+++ b/libavutil/hwcontext_vaapi.h
@@@ -1,0 -1,82 +1,82 @@@
+ /*
 - * This file is part of Libav.
++ * This file is part of FFmpeg.
+  *
 - * Libav is free software; you can redistribute it and/or
++ * FFmpeg 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,
++ * FFmpeg 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
++ * License along with FFmpeg; 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>
+ 
+ /**
+  * @file
+  * API-specific header for AV_HWDEVICE_TYPE_VAAPI.
+  *
+  * Dynamic frame pools are supported, but note that any pool used as a render
+  * target is required to be of fixed size in order to be be usable as an
+  * argument to vaCreateContext().
+  *
+  * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
+  * with the data pointer set to a VASurfaceID.
+  */
+ 
+ /**
+  * VAAPI connection details.
+  *
+  * Allocated as AVHWDeviceContext.hwctx
+  */
+ typedef struct AVVAAPIDeviceContext {
+     /**
+      * The VADisplay handle, to be filled by the user.
+      */
+     VADisplay display;
+ } AVVAAPIDeviceContext;
+ 
+ /**
+  * VAAPI-specific data associated with a frame pool.
+  *
+  * Allocated as AVHWFramesContext.hwctx.
+  */
+ typedef struct AVVAAPIFramesContext {
+     /**
+      * Set by the user to apply surface attributes to all surfaces in
+      * the frame pool.  If null, default settings are used.
+      */
+     VASurfaceAttrib *attributes;
+     int           nb_attributes;
+     /**
+      * The surfaces IDs of all surfaces in the pool after creation.
+      * Only valid if AVHWFramesContext.initial_pool_size was positive.
+      * These are intended to be used as the render_targets arguments to
+      * vaCreateContext().
+      */
+     VASurfaceID     *surface_ids;
+     int           nb_surfaces;
+ } AVVAAPIFramesContext;
+ 
+ /**
+  * VAAPI hardware pipeline configuration details.
+  *
+  * Allocated with av_hwdevice_hwconfig_alloc().
+  */
+ typedef struct AVVAAPIHWConfig {
+     /**
+      * ID of a VAAPI pipeline configuration.
+      */
+     VAConfigID config_id;
+ } AVVAAPIHWConfig;
+ 
+ #endif /* AVUTIL_HWCONTEXT_VAAPI_H */
diff --cc libavutil/version.h
index 7d8fdb3,fdd27e3..cf7458e
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@@ -63,9 -53,9 +63,9 @@@
   * @{
   */
  
 -#define LIBAVUTIL_VERSION_MAJOR 55
 -#define LIBAVUTIL_VERSION_MINOR  9
 -#define LIBAVUTIL_VERSION_MICRO  0
 +#define LIBAVUTIL_VERSION_MAJOR  55
- #define LIBAVUTIL_VERSION_MINOR  21
++#define LIBAVUTIL_VERSION_MINOR  22
 +#define LIBAVUTIL_VERSION_MICRO 100
  
  #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
                                                 LIBAVUTIL_VERSION_MINOR, \



More information about the ffmpeg-cvslog mailing list