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

Hendrik Leppkes h.leppkes at gmail.com
Mon Jan 18 01:28:54 CET 2016


On Sun, Jan 17, 2016 at 11:45 PM, Mark Thompson <sw at jkqxz.net> wrote:
> From 45a803b627d0180c1aac928756924bd39ddf529d Mon Sep 17 00:00:00 2001
> From: Mark Thompson <mrt at jkqxz.net>
> Date: Sun, 17 Jan 2016 22:13:20 +0000
> Subject: [PATCH 1/5] libavutil: some VAAPI infrastructure
>
> ---
>  configure          |   4 +
>  libavutil/Makefile |   1 +
>  libavutil/vaapi.c  | 782
> +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  libavutil/vaapi.h  | 119 ++++++++
>  4 files changed, 906 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..20bae4c
> --- /dev/null
> +++ b/libavutil/vaapi.c
> @@ -0,0 +1,782 @@
> +/*
> + * 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"
> +#include "thread.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;
> +
> +    AVMutex lock;
> +    char *device_string;
> +    int refcount;
> +    struct AVVAAPIConnection *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;
> +
> +void av_vaapi_instance_lock(AVVAAPIInstance *instance)
> +{
> +    AVVAAPIConnection *ctx = instance->connection;
> +
> +    ff_mutex_lock(&ctx->lock);
> +}
> +
> +void av_vaapi_instance_unlock(AVVAAPIInstance *instance)
> +{
> +    AVVAAPIConnection *ctx = instance->connection;
> +
> +    ff_mutex_unlock(&ctx->lock);
> +}
> +
> +static int vaapi_connection_uninit(AVVAAPIConnection *ctx)
> +{
> +    if(ctx->initialised) {
> +        vaTerminate(ctx->display);
> +        ctx->display = 0;
> +        ctx->initialised = 0;
> +        ff_mutex_destroy(&ctx->lock);
> +    }
> +
> +    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 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);
> +    }
> +
> +    ff_mutex_init(&ctx->lock, 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:
> +    vaapi_connection_uninit(ctx);
> +    return err;
> +}
> +
> +static AVVAAPIConnection *vaapi_connection_list;
> +static AVMutex vaapi_global_lock;
> +static AVOnce vaapi_global_init_control = AV_ONCE_INIT;

There is still global state here, which is a no-no.

> +
> +static void vaapi_global_init(void)
> +{
> +    vaapi_connection_list = 0;
> +    ff_mutex_init(&vaapi_global_lock, 0);
> +}
> +
> +int av_vaapi_instance_init(AVVAAPIInstance *instance, const char *device)
> +{
> +    AVVAAPIConnection *ctx;
> +    int err;
> +
> +    ff_thread_once(&vaapi_global_init_control, &vaapi_global_init);
> +
> +    ff_mutex_lock(&vaapi_global_lock);
> +
> +    for(ctx = 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;
> +        err = 0;
> +        goto done;
> +    }
> +
> +    ctx = av_mallocz(sizeof(AVVAAPIConnection));
> +    if(!ctx) {
> +        err = AVERROR(ENOMEM);
> +        goto done;
> +    }
> +
> +    err = vaapi_connection_init(ctx, device);
> +    if(err)
> +        goto done;
> +
> +    ctx->refcount = 1;
> +
> +    instance->display = ctx->display;
> +    instance->connection = ctx;
> +
> +    ctx->next = vaapi_connection_list;
> +    vaapi_connection_list = ctx;
> +
> +    av_log(ctx, AV_LOG_INFO, "New VAAPI instance (%s).\n",
> +           device ? device : "default");
> +
> +    err = 0;
> +  done:
> +    ff_mutex_unlock(&vaapi_global_lock);
> +    return err;
> +}
> +
> +int av_vaapi_instance_uninit(AVVAAPIInstance *instance)
> +{
> +    AVVAAPIConnection *ctx = instance->connection;
> +    int err;
> +
> +    ff_mutex_lock(&vaapi_global_lock);
> +
> +    if(!ctx) {
> +        err = AVERROR(EINVAL);
> +        goto done;
> +    }
> +
> +    if(ctx->refcount <= 0) {
> +        av_log(ctx, AV_LOG_ERROR, "Tried to uninit VAAPI connection with "
> +               "refcount = %d < 0.\n", ctx->refcount);
> +        err = AVERROR(EINVAL);
> +        goto done;
> +    }
> +
> +    --ctx->refcount;
> +
> +    if(ctx->refcount == 0) {
> +        AVVAAPIConnection *iter, *prev;
> +        prev = 0;
> +        for(iter = vaapi_connection_list; iter;
> +            prev = iter, iter = iter->next) {
> +            if(iter == ctx) {
> +                if(prev)
> +                    prev->next = ctx->next;
> +                else
> +                    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.
> +        }
> +
> +        vaapi_connection_uninit(ctx);
> +        av_free(ctx);
> +        memset(instance, 0, sizeof(*instance));
> +    }
> +
> +    err = 0;
> +  done:
> +    ff_mutex_unlock(&vaapi_global_lock);
> +    return err;
> +}
> +
> +
> +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..5238597
> --- /dev/null
> +++ b/libavutil/vaapi.h
> @@ -0,0 +1,119 @@
> +/*
> + * 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);
> +
> +void av_vaapi_instance_lock(AVVAAPIInstance *ctx);
> +void av_vaapi_instance_unlock(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
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list