From 45a803b627d0180c1aac928756924bd39ddf529d Mon Sep 17 00:00:00 2001 From: Mark Thompson <m...@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 <m...@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; + +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 <m...@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@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel