[FFmpeg-devel] [PATCH 1/5] libavutil: Some VAAPI infrastructure

Mark Thompson sw at jkqxz.net
Sun Jan 17 18:34:55 CET 2016


 From 2442c1aca8778167c2e60a34d03ed452737f1366 Mon Sep 17 00:00:00 2001
From: Mark Thompson <mrt at jkqxz.net>
Date: Sun, 17 Jan 2016 15:48:54 +0000
Subject: [PATCH 1/5] libavutil: Some VAAPI infrastructure

---
  configure          |   4 +
  libavutil/Makefile |   1 +
  libavutil/vaapi.c  | 732 
+++++++++++++++++++++++++++++++++++++++++++++++++++++
  libavutil/vaapi.h  | 116 +++++++++
  4 files changed, 853 insertions(+)
  create mode 100644 libavutil/vaapi.c
  create mode 100644 libavutil/vaapi.h

diff --git a/configure b/configure
index 7cef6f5..1c77015 100755
--- a/configure
+++ b/configure
@@ -5739,6 +5739,10 @@ enabled vaapi && enabled xlib &&
      check_lib2 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 &&
      enable vaapi_x11

+enabled vaapi &&
+    check_lib2 "va/va.h va/va_drm.h" vaGetDisplayDRM -lva -lva-drm &&
+    enable vaapi_drm
+
  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 bf8c713..8025f9f 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -146,6 +146,7 @@ OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o 
                     \

  OBJS-$(CONFIG_LZO)                      += lzo.o
  OBJS-$(CONFIG_OPENCL)                   += opencl.o opencl_internal.o
+OBJS-$(CONFIG_VAAPI)                    += vaapi.o

  OBJS += $(COMPAT_OBJS:%=../compat/%)

diff --git a/libavutil/vaapi.c b/libavutil/vaapi.c
new file mode 100644
index 0000000..cf0c790
--- /dev/null
+++ b/libavutil/vaapi.c
@@ -0,0 +1,732 @@
+/*
+ * VAAPI helper functions.
+ *
+ * Copyright (C) 2016 Mark Thompson <mrt at jkqxz.net>
+ *
+ * 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 <string.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "vaapi.h"
+
+#include <va/va_x11.h>
+#include <va/va_drm.h>
+
+#include "avassert.h"
+#include "imgutils.h"
+#include "pixfmt.h"
+
+
+static const AVClass vaapi_connection_class = {
+    .class_name = "VAAPI/connection",
+    .item_name  = av_default_item_name,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVClass vaapi_pipeline_class = {
+    .class_name = "VAAPI/pipeline",
+    .item_name  = av_default_item_name,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+typedef struct AVVAAPIConnection {
+    const AVClass *class;
+
+    char *device_string;
+    int refcount;
+    struct AVVAAPIInstance *next;
+
+    VADisplay display;
+    int initialised;
+    int version_major, version_minor;
+
+    enum {
+        AV_VAAPI_CONNECTION_NONE = 0,
+        AV_VAAPI_CONNECTION_DRM,
+        AV_VAAPI_CONNECTION_X11,
+        /* ?
+        AV_VAAPI_CONNECTION_GLX,
+        AV_VAAPI_CONNECTION_WAYLAND,
+        */
+    } connection_type;
+    union {
+        void *x11_display;
+        int drm_fd;
+    };
+} AVVAAPIConnection;
+
+static int av_vaapi_connection_uninit(AVVAAPIConnection *ctx)
+{
+    if(ctx->initialised) {
+        vaTerminate(ctx->display);
+        ctx->display = 0;
+        ctx->initialised = 0;
+    }
+
+    switch(ctx->connection_type) {
+
+    case AV_VAAPI_CONNECTION_DRM:
+        if(ctx->drm_fd >= 0) {
+            close(ctx->drm_fd);
+            ctx->drm_fd = -1;
+        }
+        break;
+
+    case AV_VAAPI_CONNECTION_X11:
+        if(ctx->x11_display) {
+            XCloseDisplay(ctx->x11_display);
+            ctx->x11_display = 0;
+        }
+        break;
+
+    }
+
+    return 0;
+}
+
+static int av_vaapi_connection_init(AVVAAPIConnection *ctx, const char 
*device)
+{
+    VAStatus vas;
+    int err;
+
+    ctx->class = &vaapi_connection_class;
+    if(device)
+        ctx->device_string = av_strdup(device);
+
+    // If the device name is not provided at all, assume we are in X 
and can
+    // connect to the display in DISPLAY.  If we do get a device name 
and it
+    // begins with a type indicator, use that.  Otherwise, try to guess the
+    // answer from the content of the name.
+    if(!device) {
+        ctx->connection_type = AV_VAAPI_CONNECTION_X11;
+    } else if(!strncmp(device, "drm:", 4)) {
+        ctx->connection_type = AV_VAAPI_CONNECTION_DRM;
+        device += 4;
+    } else if(!strncmp(device, "x11:", 4)) {
+        ctx->connection_type = AV_VAAPI_CONNECTION_X11;
+        device += 4;
+    } else {
+        if(strchr(device, '/')) {
+            ctx->connection_type = AV_VAAPI_CONNECTION_DRM;
+        } else if(strchr(device, ':')) {
+            ctx->connection_type = AV_VAAPI_CONNECTION_X11;
+        } else {
+            // No idea, just give up.
+            return AVERROR(EINVAL);
+        }
+    }
+
+    switch(ctx->connection_type) {
+
+    case AV_VAAPI_CONNECTION_DRM:
+        ctx->drm_fd = open(device, O_RDWR);
+        if(ctx->drm_fd < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Cannot open DRM device %s.\n",
+                   device);
+            err = AVERROR(errno);
+            goto fail;
+        }
+        ctx->display = vaGetDisplayDRM(ctx->drm_fd);
+        if(!ctx->display) {
+            av_log(ctx, AV_LOG_ERROR, "Cannot open the VA display (from 
DRM "
+                   "device %s).\n", device);
+            err = AVERROR(EINVAL);
+            goto fail;
+        }
+        break;
+
+    case AV_VAAPI_CONNECTION_X11:
+        ctx->x11_display = XOpenDisplay(device); // device might be NULL.
+        if(!ctx->x11_display) {
+            av_log(ctx, AV_LOG_ERROR, "Cannot open X11 display %s.\n",
+                   XDisplayName(device));
+            err = AVERROR(ENOENT);
+            goto fail;
+        }
+        ctx->display = vaGetDisplay(ctx->x11_display);
+        if(!ctx->display) {
+            av_log(ctx, AV_LOG_ERROR, "Cannot open the VA display (from 
X11 "
+                   "display %s).\n", XDisplayName(device));
+            err = AVERROR(EINVAL);
+            goto fail;
+        }
+        break;
+
+    default:
+        av_assert0(0);
+    }
+
+    vas = vaInitialize(ctx->display,
+                       &ctx->version_major, &ctx->version_minor);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI: %d (%s).\n",
+               vas, vaErrorStr(vas));
+        err = AVERROR(EINVAL);
+        goto fail;
+    }
+    ctx->initialised = 1;
+
+    av_log(ctx, AV_LOG_INFO, "Initialised VAAPI connection: version 
%d.%d\n",
+           ctx->version_major, ctx->version_minor);
+
+    return 0;
+
+  fail:
+    av_vaapi_connection_uninit(ctx);
+    return err;
+}
+
+static AVVAAPIConnection *av_vaapi_connection_list;
+
+int av_vaapi_instance_init(AVVAAPIInstance *instance, const char *device)
+{
+    AVVAAPIConnection *ctx;
+    int err;
+
+    for(ctx = av_vaapi_connection_list; ctx; ctx = ctx->next) {
+        if((device == 0 && ctx->device_string == 0) ||
+           (device && ctx->device_string &&
+            !strcmp(device, ctx->device_string)))
+            break;
+    }
+
+    if(ctx) {
+        av_log(ctx, AV_LOG_INFO, "New VAAPI instance connected to 
existing "
+               "instance (%s).\n", device ? device : "default");
+        ++ctx->refcount;
+        instance->connection = ctx;
+        instance->display = ctx->display;
+        return 0;
+    }
+
+    ctx = av_mallocz(sizeof(AVVAAPIConnection));
+    if(!ctx)
+        return AVERROR(ENOMEM);
+
+    err = av_vaapi_connection_init(ctx, device);
+    if(err)
+        return err;
+
+    instance->display = ctx->display;
+    ctx->next = av_vaapi_connection_list;
+    av_vaapi_connection_list = ctx;
+
+    av_log(ctx, AV_LOG_INFO, "New VAAPI instance (%s).\n",
+           device ? device : "default");
+
+    return 0;
+}
+
+int av_vaapi_instance_uninit(AVVAAPIInstance *instance)
+{
+    AVVAAPIConnection *ctx = instance->connection;
+
+    if(!ctx)
+        return AVERROR(EINVAL);
+
+    if(ctx->refcount <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "Tried to uninit VAAPI connection with "
+               "refcount = %d < 0.\n", ctx->refcount);
+        return AVERROR(EINVAL);
+    }
+
+    --ctx->refcount;
+
+    if(ctx->refcount == 0) {
+        AVVAAPIConnection *iter, *prev;
+        prev = 0;
+        for(iter = av_vaapi_connection_list; iter;
+            prev = iter, iter = iter->next) {
+            if(iter == ctx) {
+                if(prev)
+                    prev->next = ctx->next;
+                else
+                    av_vaapi_connection_list = ctx->next;
+                break;
+            }
+        }
+        if(!iter) {
+            av_log(ctx, AV_LOG_WARNING, "Tried to uninit VAAPI connection "
+                   "not in connection list?\n");
+            // Not fatal.
+        }
+
+        av_vaapi_connection_uninit(ctx);
+        av_free(ctx);
+        memset(instance, 0, sizeof(*instance));
+    }
+
+    return 0;
+}
+
+
+static int vaapi_create_surfaces(AVVAAPIInstance *instance,
+                                 AVVAAPISurfaceConfig *config,
+                                 AVVAAPISurface *surfaces,
+                                 VASurfaceID *ids)
+{
+    VAStatus vas;
+    int i;
+
+    vas = vaCreateSurfaces(instance->display, config->rt_format,
+                           config->width, config->height, ids, 
config->count,
+                           config->attributes, config->attribute_count);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(instance->connection, AV_LOG_ERROR, "Failed to create "
+               "surfaces: %d (%s).\n", vas, vaErrorStr(vas));
+        return AVERROR(EINVAL);
+    }
+
+    for(i = 0; i < config->count; i++) {
+        surfaces[i].id       = ids[i];
+        surfaces[i].refcount = 0;
+        surfaces[i].instance = instance;
+        surfaces[i].config   = config;
+        av_log(instance->connection, AV_LOG_TRACE, "Created VA surface "
+               "%d: %#x.\n", i, surfaces[i].id);
+    }
+
+    return 0;
+}
+
+static void vaapi_destroy_surfaces(AVVAAPIInstance *instance,
+                                   AVVAAPISurfaceConfig *config,
+                                   AVVAAPISurface *surfaces,
+                                   VASurfaceID *ids)
+{
+    VAStatus vas;
+    int i;
+
+    for(i = 0; i < config->count; i++) {
+        av_assert0(surfaces[i].id == ids[i]);
+        if(surfaces[i].refcount > 0)
+            av_log(instance->connection, AV_LOG_WARNING, "Destroying "
+                   "surface %#x which is still in use.\n", surfaces[i].id);
+        av_assert0(surfaces[i].instance == instance);
+        av_assert0(surfaces[i].config == config);
+    }
+
+    vas = vaDestroySurfaces(instance->display, ids, config->count);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(instance, AV_LOG_ERROR, "Failed to destroy surfaces: "
+               "%d (%s).\n", vas, vaErrorStr(vas));
+    }
+}
+
+int av_vaapi_pipeline_init(AVVAAPIPipelineContext *ctx,
+                           AVVAAPIInstance *instance,
+                           AVVAAPIPipelineConfig *config,
+                           AVVAAPISurfaceConfig *input,
+                           AVVAAPISurfaceConfig *output)
+{
+    VAStatus vas;
+    int err;
+
+    // Currently this only supports a pipeline which actually creates
+    // output surfaces.  An intra-only encoder (e.g. JPEG) won't, so
+    // some modification would be required to make that work.
+    if(!output)
+        return AVERROR(EINVAL);
+
+    memset(ctx, 0, sizeof(*ctx));
+    ctx->class = &vaapi_pipeline_class;
+
+    ctx->instance = instance;
+    ctx->config   = config;
+
+    vas = vaCreateConfig(instance->display, config->profile,
+                         config->entrypoint, config->attributes,
+                         config->attribute_count, &ctx->config_id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to create pipeline 
configuration: "
+               "%d (%s).\n", vas, vaErrorStr(vas));
+        err = AVERROR(EINVAL);
+        goto fail_config;
+    }
+
+    if(input) {
+        ctx->input_surfaces = av_calloc(input->count, 
sizeof(AVVAAPISurface));
+        if(!ctx->input_surfaces) {
+            err = AVERROR(ENOMEM);
+            goto fail_alloc_input_surfaces;
+        }
+
+        err = vaapi_create_surfaces(instance, input, ctx->input_surfaces,
+                                    ctx->input_surface_ids);
+        if(err)
+            goto fail_create_input_surfaces;
+        ctx->input = input;
+    } else {
+        av_log(ctx, AV_LOG_INFO, "No input surfaces.\n");
+        ctx->input = 0;
+    }
+
+    if(output) {
+        ctx->output_surfaces = av_calloc(output->count, 
sizeof(AVVAAPISurface));
+        if(!ctx->output_surfaces) {
+            err = AVERROR(ENOMEM);
+            goto fail_alloc_output_surfaces;
+        }
+
+        err = vaapi_create_surfaces(instance, output, ctx->output_surfaces,
+                                    ctx->output_surface_ids);
+        if(err)
+            goto fail_create_output_surfaces;
+        ctx->output = output;
+    } else {
+        av_log(ctx, AV_LOG_INFO, "No output surfaces.\n");
+        ctx->output = 0;
+    }
+
+    vas = vaCreateContext(instance->display, ctx->config_id,
+                              output->width, output->height,
+                              VA_PROGRESSIVE,
+                              ctx->output_surface_ids, output->count,
+                              &ctx->context_id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to create pipeline context: "
+               "%d (%s).\n", vas, vaErrorStr(vas));
+        err = AVERROR(EINVAL);
+        goto fail_context;
+    }
+
+    av_log(ctx, AV_LOG_INFO, "VAAPI pipeline initialised: config %#x "
+           "context %#x.\n", ctx->config_id, ctx->context_id);
+    if(input)
+        av_log(ctx, AV_LOG_INFO, "  Input: %u surfaces of %ux%u.\n",
+               input->count, input->width, input->height);
+    if(output)
+        av_log(ctx, AV_LOG_INFO, "  Output: %u surfaces of %ux%u.\n",
+               output->count, output->width, output->height);
+
+    return 0;
+
+  fail_context:
+    vaapi_destroy_surfaces(instance, output, ctx->output_surfaces,
+                           ctx->output_surface_ids);
+  fail_create_output_surfaces:
+    av_freep(&ctx->output_surfaces);
+  fail_alloc_output_surfaces:
+    vaapi_destroy_surfaces(instance, input, ctx->input_surfaces,
+                           ctx->input_surface_ids);
+  fail_create_input_surfaces:
+    av_freep(&ctx->input_surfaces);
+  fail_alloc_input_surfaces:
+    vaDestroyConfig(instance->display, ctx->config_id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to destroy pipeline "
+               "configuration: %d (%s).\n", vas, vaErrorStr(vas));
+    }
+  fail_config:
+    return err;
+}
+
+int av_vaapi_pipeline_uninit(AVVAAPIPipelineContext *ctx)
+{
+    VAStatus vas;
+
+    av_assert0(ctx->instance);
+    av_assert0(ctx->config);
+
+    vas = vaDestroyContext(ctx->instance->display, ctx->context_id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to destroy pipeline context: "
+               "%d (%s).\n", vas, vaErrorStr(vas));
+    }
+
+    if(ctx->output) {
+        vaapi_destroy_surfaces(ctx->instance, ctx->output,
+                               ctx->output_surfaces,
+                               ctx->output_surface_ids);
+        av_freep(&ctx->output_surfaces);
+    }
+
+    if(ctx->input) {
+        vaapi_destroy_surfaces(ctx->instance, ctx->input,
+                               ctx->input_surfaces,
+                               ctx->input_surface_ids);
+        av_freep(&ctx->input_surfaces);
+    }
+
+    vaDestroyConfig(ctx->instance->display, ctx->config_id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to destroy pipeline 
configuration: "
+               "%d (%s).\n", vas, vaErrorStr(vas));
+    }
+
+    return 0;
+}
+
+static void vaapi_codec_release_surface(void *opaque, uint8_t *data)
+{
+    AVVAAPISurface *surface = opaque;
+
+    av_assert0(surface->refcount > 0);
+    --surface->refcount;
+}
+
+static int vaapi_get_surface(AVVAAPIPipelineContext *ctx,
+                             AVVAAPISurfaceConfig *config,
+                             AVVAAPISurface *surfaces, AVFrame *frame)
+{
+    AVVAAPISurface *surface;
+    int i;
+
+    for(i = 0; i < config->count; i++) {
+        if(surfaces[i].refcount == 0)
+            break;
+    }
+    if(i >= config->count) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to allocate surface "
+               "(%d in use).\n", config->count);
+        return AVERROR(ENOMEM);
+    }
+    surface = &surfaces[i];
+
+    ++surface->refcount;
+    frame->data[3] = (uint8_t*)(uintptr_t)surface->id;
+    frame->buf[0] = av_buffer_create((uint8_t*)surface, 0,
+                                     &vaapi_codec_release_surface,
+                                     surface, AV_BUFFER_FLAG_READONLY);
+    if(!frame->buf[0]) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to allocate dummy buffer "
+               "for surface %#x.\n", surface->id);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->format = AV_PIX_FMT_VAAPI;
+    frame->width  = config->width;
+    frame->height = config->height;
+
+    return 0;
+}
+
+int av_vaapi_get_input_surface(AVVAAPIPipelineContext *ctx, AVFrame *frame)
+{
+    return vaapi_get_surface(ctx, ctx->input, ctx->input_surfaces, frame);
+}
+
+int av_vaapi_get_output_surface(AVVAAPIPipelineContext *ctx, AVFrame 
*frame)
+{
+    return vaapi_get_surface(ctx, ctx->output, ctx->output_surfaces, 
frame);
+}
+
+
+int av_vaapi_map_surface(AVVAAPISurface *surface, int get)
+{
+    AVVAAPIInstance *instance = surface->instance;
+    AVVAAPISurfaceConfig *config = surface->config;
+    VAStatus vas;
+    int err;
+    void *address;
+    // On current Intel drivers, derive gives you memory which is very slow
+    // to read (uncached?).  It can be better for write-only cases, but for
+    // now play it safe and never use derive.
+    int derive = 0;
+
+    vas = vaSyncSurface(instance->display, surface->id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(instance, AV_LOG_ERROR, "Failed to sync surface "
+               "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+        err = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if(derive) {
+        vas = vaDeriveImage(instance->display,
+                            surface->id, &surface->image);
+        if(vas != VA_STATUS_SUCCESS) {
+            av_log(instance, AV_LOG_ERROR, "Failed to derive image from 
surface "
+                   "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+            derive = 0;
+        }
+    }
+    if(!derive) {
+        vas = vaCreateImage(instance->display,
+                            &config->image_format,
+                            config->width, config->height,
+                            &surface->image);
+        if(vas != VA_STATUS_SUCCESS) {
+            av_log(instance, AV_LOG_ERROR, "Failed to create image for 
surface "
+                   "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+            err = AVERROR(EINVAL);
+            goto fail;
+        }
+
+        if(get) {
+            vas = vaGetImage(instance->display,
+                             surface->id, 0, 0,
+                             config->width, config->height,
+                             surface->image.image_id);
+            if(vas != VA_STATUS_SUCCESS) {
+                av_log(instance, AV_LOG_ERROR, "Failed to get image for 
surface "
+                       "%#x: %d (%s).\n", surface->id, vas, 
vaErrorStr(vas));
+                err = AVERROR(EINVAL);
+                goto fail_image;
+            }
+        }
+    }
+
+    av_assert0(surface->image.format.fourcc == 
config->image_format.fourcc);
+
+    vas = vaMapBuffer(instance->display,
+                      surface->image.buf, &address);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(instance, AV_LOG_ERROR, "Failed to map image from surface "
+               "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+        err = AVERROR(EINVAL);
+        goto fail_image;
+    }
+
+    surface->mapped_address = address;
+
+    return 0;
+
+  fail_image:
+    vas = vaDestroyImage(instance->display, surface->image.image_id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(instance, AV_LOG_ERROR, "Failed to destroy image for 
surface "
+               "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+    }
+  fail:
+    return err;
+}
+
+int av_vaapi_unmap_surface(AVVAAPISurface *surface, int put)
+{
+    AVVAAPIInstance *instance = surface->instance;
+    AVVAAPISurfaceConfig *config = surface->config;
+    VAStatus vas;
+    int derive = 0;
+
+    surface->mapped_address = 0;
+
+    vas = vaUnmapBuffer(instance->display,
+                        surface->image.buf);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(instance, AV_LOG_ERROR, "Failed to unmap image from 
surface "
+               "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+    }
+
+    if(!derive && put) {
+        vas = vaPutImage(instance->display, surface->id,
+                         surface->image.image_id,
+                         0, 0, config->width, config->height,
+                         0, 0, config->width, config->height);
+        if(vas != VA_STATUS_SUCCESS) {
+            av_log(instance, AV_LOG_ERROR, "Failed to put image for 
surface "
+                   "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+        }
+    }
+
+    vas = vaDestroyImage(instance->display,
+                         surface->image.image_id);
+    if(vas != VA_STATUS_SUCCESS) {
+        av_log(instance, AV_LOG_ERROR, "Failed to destroy image for 
surface "
+               "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
+    }
+
+    return 0;
+}
+
+int av_vaapi_copy_to_surface(const AVFrame *f, AVVAAPISurface *surface)
+{
+    VAImage *image = &surface->image;
+    char *data = surface->mapped_address;
+    av_assert0(data);
+
+    switch(f->format) {
+
+    case AV_PIX_FMT_YUV420P:
+        av_assert0(image->format.fourcc == VA_FOURCC_YV12);
+        av_image_copy_plane(data + image->offsets[0], image->pitches[0],
+                            f->data[0], f->linesize[0],
+                            f->width, f->height);
+        av_image_copy_plane(data + image->offsets[1], image->pitches[1],
+                            f->data[2], f->linesize[2],
+                            f->width / 2, f->height / 2);
+        av_image_copy_plane(data + image->offsets[2], image->pitches[2],
+                            f->data[1], f->linesize[1],
+                            f->width / 2, f->height / 2);
+        break;
+
+    case AV_PIX_FMT_NV12:
+        av_assert0(image->format.fourcc == VA_FOURCC_NV12);
+        av_image_copy_plane(data + image->offsets[0], image->pitches[0],
+                            f->data[0], f->linesize[0],
+                            f->width, f->height);
+        av_image_copy_plane(data + image->offsets[1], image->pitches[1],
+                            f->data[1], f->linesize[1],
+                            f->width, f->height / 2);
+        break;
+
+    case AV_PIX_FMT_BGR0:
+        av_assert0(image->format.fourcc == VA_FOURCC_BGRX);
+        av_image_copy_plane(data + image->offsets[0], image->pitches[0],
+                            f->data[0], f->linesize[0],
+                            f->width * 4, f->height);
+        break;
+
+    default:
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+int av_vaapi_copy_from_surface(AVFrame *f, AVVAAPISurface *surface)
+{
+    VAImage *image = &surface->image;
+    char *data = surface->mapped_address;
+    av_assert0(data);
+
+    switch(f->format) {
+
+    case AV_PIX_FMT_YUV420P:
+        av_assert0(image->format.fourcc == VA_FOURCC_YV12);
+        av_image_copy_plane(f->data[0], f->linesize[0],
+                            data + image->offsets[0], image->pitches[0],
+                            f->width, f->height);
+        // Um, apparently these are not the same way round...
+        av_image_copy_plane(f->data[2], f->linesize[2],
+                            data + image->offsets[1], image->pitches[1],
+                            f->width / 2, f->height / 2);
+        av_image_copy_plane(f->data[1], f->linesize[1],
+                            data + image->offsets[2], image->pitches[2],
+                            f->width / 2, f->height / 2);
+        break;
+
+    case AV_PIX_FMT_NV12:
+        av_assert0(image->format.fourcc == VA_FOURCC_NV12);
+        av_image_copy_plane(f->data[0], f->linesize[0],
+                            data + image->offsets[0], image->pitches[0],
+                            f->width, f->height);
+        av_image_copy_plane(f->data[1], f->linesize[1],
+                            data + image->offsets[1], image->pitches[1],
+                            f->width, f->height / 2);
+        break;
+
+    default:
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
diff --git a/libavutil/vaapi.h b/libavutil/vaapi.h
new file mode 100644
index 0000000..18e15ce
--- /dev/null
+++ b/libavutil/vaapi.h
@@ -0,0 +1,116 @@
+/*
+ * VAAPI helper functions.
+ *
+ * Copyright (C) 2016 Mark Thompson <mrt at jkqxz.net>
+ *
+ * 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 LIBAVUTIL_VAAPI_H_
+#define LIBAVUTIL_VAAPI_H_
+
+#include <va/va.h>
+
+#include "pixfmt.h"
+#include "frame.h"
+
+
+typedef struct AVVAAPIInstance {
+    VADisplay display;
+
+    void *connection;
+} AVVAAPIInstance;
+
+
+int av_vaapi_instance_init(AVVAAPIInstance *ctx, const char *device);
+int av_vaapi_instance_uninit(AVVAAPIInstance *ctx);
+
+
+#define AV_VAAPI_MAX_SURFACES 64
+
+
+typedef struct AVVAAPISurfaceConfig {
+    enum AVPixelFormat av_format;
+    unsigned int rt_format;
+    VAImageFormat image_format;
+
+    unsigned int count;
+    unsigned int width;
+    unsigned int height;
+
+    unsigned int attribute_count;
+    VASurfaceAttrib *attributes;
+} AVVAAPISurfaceConfig;
+
+typedef struct AVVAAPISurface {
+    VASurfaceID id;
+    int refcount;
+
+    VAImage image;
+    void *mapped_address;
+
+    AVVAAPIInstance *instance;
+    AVVAAPISurfaceConfig *config;
+} AVVAAPISurface;
+
+
+typedef struct AVVAAPIPipelineConfig {
+    VAProfile profile;
+    VAEntrypoint entrypoint;
+
+    unsigned int attribute_count;
+    VAConfigAttrib *attributes;
+} AVVAAPIPipelineConfig;
+
+typedef struct AVVAAPIPipelineContext {
+    const AVClass *class;
+
+    AVVAAPIInstance *instance;
+    AVVAAPIPipelineConfig *config;
+    AVVAAPISurfaceConfig *input;
+    AVVAAPISurfaceConfig *output;
+
+    VAConfigID config_id;
+    VAContextID context_id;
+
+    AVVAAPISurface *input_surfaces;
+    VASurfaceID input_surface_ids[AV_VAAPI_MAX_SURFACES];
+
+    AVVAAPISurface *output_surfaces;
+    VASurfaceID output_surface_ids[AV_VAAPI_MAX_SURFACES];
+} AVVAAPIPipelineContext;
+
+
+int av_vaapi_pipeline_init(AVVAAPIPipelineContext *ctx,
+                           AVVAAPIInstance *instance,
+                           AVVAAPIPipelineConfig *config,
+                           AVVAAPISurfaceConfig *input,
+                           AVVAAPISurfaceConfig *output);
+int av_vaapi_pipeline_uninit(AVVAAPIPipelineContext *ctx);
+
+int av_vaapi_get_input_surface(AVVAAPIPipelineContext *ctx, AVFrame 
*frame);
+int av_vaapi_get_output_surface(AVVAAPIPipelineContext *ctx, AVFrame 
*frame);
+
+int av_vaapi_map_surface(AVVAAPISurface *surface, int get);
+int av_vaapi_unmap_surface(AVVAAPISurface *surface, int put);
+
+
+int av_vaapi_copy_to_surface(const AVFrame *f, AVVAAPISurface *surface);
+int av_vaapi_copy_from_surface(AVFrame *f, AVVAAPISurface *surface);
+
+
+#endif /* LIBAVUTIL_VAAPI_H_ */
-- 
2.6.4




More information about the ffmpeg-devel mailing list