On Sun, Jan 17, 2016 at 11:45 PM, Mark Thompson <s...@jkqxz.net> wrote: > 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;
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 <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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel