[FFmpeg-devel] [PATCH v4 1/5] libavutil: VAAPI infrastructure
wm4
nfxjfg at googlemail.com
Sun Jan 24 13:29:23 CET 2016
On Sat, 23 Jan 2016 19:13:38 +0000
Mark Thompson <sw at jkqxz.net> wrote:
> ---
> configure | 5 +
> libavutil/Makefile | 1 +
> libavutil/vaapi.c | 546 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> libavutil/vaapi.h | 115 +++++++++++
> 4 files changed, 667 insertions(+)
> create mode 100644 libavutil/vaapi.c
> create mode 100644 libavutil/vaapi.h
>
> diff --git a/configure b/configure
> index 8f4642b..cd386b4 100755
> --- a/configure
> +++ b/configure
> @@ -2042,6 +2042,7 @@ CONFIG_EXTRA="
> texturedsp
> texturedspenc
> tpeldsp
> + vaapi_recent
> videodsp
> vp3dsp
> vp56dsp
> @@ -5740,6 +5741,10 @@ enabled vaapi &&
> check_lib va/va.h vaInitialize -lva ||
> disable vaapi
>
> +enabled vaapi &&
> + check_code cc va/va.h "vaCreateSurfaces(0, 0, 0, 0, 0, 0, 0, 0)" &&
> + enable vaapi_recent
> +
> enabled vaapi && enabled xlib &&
> check_lib2 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 &&
> enable vaapi_x11
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index 65b2d25..9d6a313 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -148,6 +148,7 @@ OBJS-$(!HAVE_ATOMICS_NATIVE) += atomic.o \
>
> OBJS-$(CONFIG_LZO) += lzo.o
> OBJS-$(CONFIG_OPENCL) += opencl.o opencl_internal.o
> +OBJS-$(CONFIG_VAAPI_RECENT) += vaapi.o
>
> OBJS += $(COMPAT_OBJS:%=../compat/%)
>
> diff --git a/libavutil/vaapi.c b/libavutil/vaapi.c
> new file mode 100644
> index 0000000..d5e67c4
> --- /dev/null
> +++ b/libavutil/vaapi.c
> @@ -0,0 +1,546 @@
> +/*
> + * 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 "vaapi.h"
> +
> +#include "avassert.h"
> +#include "imgutils.h"
> +#include "pixfmt.h"
> +
> +
> +AVVAAPIHardwareContext *av_vaapi_alloc_hardware_context(void)
> +{
> + return av_mallocz(sizeof(AVVAAPIHardwareContext));
> +}
> +
> +void av_vaapi_lock_hardware_context(AVVAAPIHardwareContext *ctx)
> +{
> + if(ctx->lock)
> + ctx->lock(ctx->lock_user_context);
> +}
> +
> +void av_vaapi_unlock_hardware_context(AVVAAPIHardwareContext *ctx)
> +{
> + if(ctx->unlock)
> + ctx->unlock(ctx->lock_user_context);
> +}
> +
> +
> +typedef struct AVVAAPISurface {
> + VASurfaceID id;
> + AVVAAPIHardwareContext *hardware_context;
> +
> + VAImage image;
> + void *mapped_address;
> +} AVVAAPISurface;
> +
> +static AVVAAPISurface *vaapi_get_surface(const AVFrame *frame)
> +{
> + av_assert0(frame);
> + av_assert0(frame->buf[0]);
> + av_assert0(frame->buf[0]->data);
> + return (AVVAAPISurface*)frame->buf[0]->data;
> +}
> +
> +static AVVAAPISurfaceConfig *vaapi_get_surface_config(const AVFrame *frame)
> +{
> + av_assert0(frame);
> + av_assert0(frame->buf[1]);
> + av_assert0(frame->buf[1]->data);
> + return (AVVAAPISurfaceConfig*)frame->buf[1]->data;
> +}
> +
> +static void vaapi_surface_free(void *opaque, uint8_t *data)
> +{
> + AVVAAPISurface *surface = (AVVAAPISurface*)data;
> + AVVAAPIHardwareContext *hw_ctx = surface->hardware_context;
> + VAStatus vas;
> +
> + av_vaapi_lock_hardware_context(hw_ctx);
> +
> + vas = vaDestroySurfaces(surface->hardware_context->display,
> + &surface->id, 1);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to destroy surface: "
> + "%d (%s).\n", vas, vaErrorStr(vas));
I second that av_log should take a context in as many cases as possible.
> + }
> +
> + av_free(surface);
> +
> + av_vaapi_unlock_hardware_context(hw_ctx);
> +}
> +
> +int av_vaapi_surface_pool_init(AVVAAPISurfacePool *pool,
> + AVVAAPIHardwareContext *hw_ctx,
> + AVVAAPISurfaceConfig *config,
> + int frame_count)
> +{
> + AVBufferRef *config_buffer;
> + AVVAAPISurface *surface;
> + AVFrame *frame;
> + VAStatus vas;
> + int i, err;
> +
> + memset(pool, 0, sizeof(*pool));
> +
> + pool->hardware_context = hw_ctx;
> + pool->frame_count = frame_count;
> +
> + config_buffer = av_buffer_alloc(sizeof(*config));
> + if(!config_buffer)
> + return AVERROR(ENOMEM);
> + memcpy(config_buffer->data, config, sizeof(*config));
> + config = (AVVAAPISurfaceConfig*)config_buffer->data;
> +
> + av_vaapi_lock_hardware_context(hw_ctx);
> +
> + for(i = 0; i < frame_count; i++) {
> + frame = av_frame_alloc();
> + if(!frame) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> + surface = av_mallocz(sizeof(*surface));
> + if(!surface) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + surface->hardware_context = hw_ctx;
> +
> + vas = vaCreateSurfaces(hw_ctx->display, config->rt_format,
> + config->width, config->height,
> + &surface->id, 1,
> + config->attributes, config->attribute_count);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to create surface: "
> + "%d (%s).\n", vas, vaErrorStr(vas));
> + err = AVERROR_EXTERNAL;
> + goto fail;
> + }
> +
> + frame->buf[0] = av_buffer_create((uint8_t*)surface, sizeof(*surface),
> + &vaapi_surface_free,
> + 0, AV_BUFFER_FLAG_READONLY);
> + if(!frame->buf[0]) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + frame->buf[1] = av_buffer_ref(config_buffer);
> + if(!frame->buf[1]) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + frame->data[3] = (uint8_t*)(uintptr_t)surface->id;
> +
> + frame->format = AV_PIX_FMT_VAAPI;
> + frame->width = config->width;
> + frame->height = config->height;
> +
> + pool->frames[i] = frame;
> + }
> +
> + for(; i < FF_ARRAY_ELEMS(pool->frames); i++)
> + pool->frames[i] = 0;
> +
> + av_buffer_unref(&config_buffer);
> +
> + av_log(0, AV_LOG_DEBUG, "Surface pool initialised: %u surfaces of %ux%u.\n",
> + pool->frame_count, config->width, config->height);
> +
> + err = 0;
> + fail:
Doesn't the failure path fail to free already allocated resources?
> + av_vaapi_unlock_hardware_context(hw_ctx);
> + return err;
> +}
> +
> +int av_vaapi_surface_pool_uninit(AVVAAPISurfacePool *pool)
> +
> +{
> + int i;
> +
> + av_vaapi_lock_hardware_context(pool->hardware_context);
> +
> + for(i = 0; i < FF_ARRAY_ELEMS(pool->frames); i++) {
> + if(pool->frames[i])
> + av_frame_free(&pool->frames[i]);
> + }
> +
> + av_vaapi_unlock_hardware_context(pool->hardware_context);
> +
> + return 0;
> +}
> +
> +int av_vaapi_surface_pool_get(AVVAAPISurfacePool *pool, AVFrame *target)
> +{
> + AVFrame *frame = 0;
> + int i, err;
> +
> + av_vaapi_lock_hardware_context(pool->hardware_context);
> +
> + for(i = 0; i < FF_ARRAY_ELEMS(pool->frames); i++) {
> + if(!pool->frames[i])
> + break;
> +
> + if(av_buffer_get_ref_count(pool->frames[i]->buf[0]) == 1) {
> + frame = pool->frames[i];
Checking the refcount explicitly doesn't sound like a very good
solution, but I acknowledge that it's pretty simple, and a more
"proper" solution would be rather complex, so I don't mind.
> + break;
> + }
> + }
> +
> + if(frame) {
> + err = av_frame_ref(target, frame);
> + } else {
> + err = AVERROR(ENOMEM);
> + }
> +
> + av_vaapi_unlock_hardware_context(pool->hardware_context);
> +
> + return err;
> +}
> +
> +int av_vaapi_map_frame(AVFrame *frame, int get)
> +{
> + AVVAAPISurface *surface = vaapi_get_surface(frame);
> + AVVAAPISurfaceConfig *config = vaapi_get_surface_config(frame);
> + AVVAAPIHardwareContext *hw_ctx = surface->hardware_context;
> + VAImage *image = &surface->image;
> + VAStatus vas;
> + int i, 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.
Still subject to debate when we introduce a "GPU memcpy".
I think someone on this list once even posted a patch to add such a
memcpy, but it didn't get in yet.
> + int derive = 0;
> +
> + if(surface->mapped_address) {
> + av_log(0, AV_LOG_ERROR, "Surface %#x already mapped.\n",
> + surface->id);
> + // This could refcount somehow to allow multiple mappings?
> + return AVERROR(EINVAL);
> + }
> +
> + av_vaapi_lock_hardware_context(hw_ctx);
> +
> + vas = vaSyncSurface(hw_ctx->display, surface->id);
Is this strictly needed? (I don't know.)
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to sync surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + err = AVERROR_EXTERNAL;
> + goto fail;
> + }
> +
> + if(derive) {
> + vas = vaDeriveImage(hw_ctx->display,
> + surface->id, image);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to derive image from surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
Is it guaranteed that surface->id is an unsigned int and VAStatus is
int? I know the typedefs resolve to these types, but is there a chance
a future libva version will change them?
Also, this message should probably not be an error, but a verbose at
most. (Since it can gracefully fallback to the code path below.)
> + derive = 0;
> + }
> + }
> + if(!derive) {
> + vas = vaCreateImage(hw_ctx->display, &config->image_format,
> + config->width, config->height, image);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to create image for surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + err = AVERROR_EXTERNAL;
> + goto fail;
> + }
> +
> + if(get) {
> + vas = vaGetImage(hw_ctx->display, surface->id, 0, 0,
> + config->width, config->height, image->image_id);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to get image for surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + err = AVERROR_EXTERNAL;
> + goto fail_image;
> + }
> + }
> + }
> +
> + av_assert0(image->format.fourcc == config->image_format.fourcc);
I wouldn't use assert to check expectations from API return values.
> +
> + vas = vaMapBuffer(hw_ctx->display, image->buf, &address);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to map image from surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + err = AVERROR_EXTERNAL;
> + goto fail_image;
> + }
> +
> + surface->mapped_address = address;
> +
> + for(i = 0; i < image->num_planes; i++) {
> + frame->data[i] = (uint8_t*)address + image->offsets[i];
> + frame->linesize[i] = image->pitches[i];
> + }
> +
> + av_vaapi_unlock_hardware_context(hw_ctx);
> + return 0;
> +
> + fail_image:
> + vas = vaDestroyImage(hw_ctx->display, surface->image.image_id);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to destroy image for surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + }
> + fail:
> + av_vaapi_unlock_hardware_context(hw_ctx);
> + return err;
> +}
So this API function uses a per-surface image struct and other fields.
What happens if multiple threads try to map it? Does libva even allow
this in theory? (If not, this isn't a concern. But maybe the function
could complain loudly if such an use is detected.)
> +
> +int av_vaapi_unmap_frame(AVFrame *frame, int put)
> +{
> + AVVAAPISurface *surface = vaapi_get_surface(frame);
> + AVVAAPISurfaceConfig *config = vaapi_get_surface_config(frame);
> + AVVAAPIHardwareContext *hw_ctx = surface->hardware_context;
> + VAImage *image = &surface->image;
> + VAStatus vas;
> + int i;
> + int derive = 0;
> +
> + surface->mapped_address = 0;
> +
> + for(i = 0; i < image->num_planes; i++) {
> + frame->data[i] = 0;
> + frame->linesize[i] = 0;
> + }
> +
> + vas = vaUnmapBuffer(hw_ctx->display, image->buf);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to unmap image from surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + }
> +
> + if(!derive && put) {
> + vas = vaPutImage(hw_ctx->display, surface->id, image->image_id,
> + 0, 0, config->width, config->height,
> + 0, 0, config->width, config->height);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to put image for surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + }
> + }
> +
> + vas = vaDestroyImage(hw_ctx->display,
> + surface->image.image_id);
> + if(vas != VA_STATUS_SUCCESS) {
> + av_log(0, AV_LOG_ERROR, "Failed to destroy image for surface "
> + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas));
> + }
> +
> + return 0;
> +}
> +
> +static enum AVPixelFormat vaapi_pix_fmt(unsigned int fourcc)
> +{
> + switch(fourcc) {
> + case VA_FOURCC_NV12: return AV_PIX_FMT_NV12;
> + case VA_FOURCC_IYUV: return AV_PIX_FMT_YUV420P;
> + case VA_FOURCC_YV12: return AV_PIX_FMT_YUV420P; // U/V planes swapped.
> + case VA_FOURCC_BGRA: return AV_PIX_FMT_BGRA;
> + case VA_FOURCC_BGRX: return AV_PIX_FMT_BGR0;
> + case VA_FOURCC_RGBA: return AV_PIX_FMT_RGBA;
> + case VA_FOURCC_RGBX: return AV_PIX_FMT_RGB0;
> + default:
> + return AV_PIX_FMT_NONE;
> + }
> +}
> +
> +static AVFrame *vaapi_make_proxy_frame(const AVFrame *src)
> +{
> + AVVAAPISurface *surface = vaapi_get_surface(src);
> + VAImage *image = &surface->image;
> + AVFrame *dst;
> + int i;
> +
> + if(!surface->mapped_address) {
> + av_log(0, AV_LOG_ERROR, "Surface %#x is not mapped.",
> + surface->id);
> + return 0;
> + }
> +
> + dst = av_frame_alloc();
> + if(!dst)
> + return 0;
> +
> + for(i = 0; i < image->num_planes; i++) {
> + dst->data[i] = src->data[i];
> + dst->linesize[i] = src->linesize[i];
> + }
> +
> + dst->width = src->width;
> + dst->height = src->height;
> +
> + dst->format = vaapi_pix_fmt(image->format.fourcc);
> + if(image->format.fourcc == VA_FOURCC_YV12) {
> + uint8_t *tmp;
> + tmp = dst->data[1];
> + dst->data[1] = dst->data[2];
> + dst->data[2] = tmp;
> + }
> +
> + av_frame_copy_props(dst, src);
> +
> + return dst;
> +}
So this just swaps the planes if needed? Maybe it would be better to
make av_vaapi_map_frame() do this? (On the other hand, it could be
confusing... but I think it'd be better in summary.)
> +
> +int av_vaapi_copy_to_surface(AVFrame *dst, const AVFrame *src)
> +{
> + AVFrame *proxy;
> + int err;
> +
> + if(dst->format != AV_PIX_FMT_VAAPI)
> + return AVERROR(EINVAL);
> +
> + err = av_vaapi_map_frame(dst, 0);
> + if(err)
> + return err;
> +
> + proxy = vaapi_make_proxy_frame(dst);
> + if(proxy)
> + err = av_frame_copy(proxy, src);
> + else
> + err = AVERROR(ENOMEM);
> +
> + av_vaapi_unmap_frame(dst, 1);
> +
> + return 0;
> +}
> +
> +int av_vaapi_copy_from_surface(AVFrame *dst, AVFrame *src)
> +{
> + AVFrame *proxy;
> + int err;
> +
> + if(src->format != AV_PIX_FMT_VAAPI)
> + return AVERROR(EINVAL);
> +
> + err = av_vaapi_map_frame(src, 1);
> + if(err)
> + return err;
> +
> + proxy = vaapi_make_proxy_frame(src);
> + if(proxy)
> + err = av_frame_copy(dst, proxy);
> + else
> + err = AVERROR(ENOMEM);
> +
> + av_vaapi_unmap_frame(src, 0);
> +
> + return err;
> +}
> +
> +static const AVClass vaapi_pipeline_class = {
> + .class_name = "VAAPI/pipeline",
> + .item_name = av_default_item_name,
> + .version = LIBAVUTIL_VERSION_INT,
> +};
> +
> +int av_vaapi_pipeline_init(AVVAAPIPipelineContext *ctx,
> + AVVAAPIHardwareContext *hw_ctx,
> + AVVAAPIPipelineConfig *config,
> + AVVAAPISurfacePool *pool)
> +{
> + VASurfaceID output_surface_ids[AV_VAAPI_MAX_SURFACES];
> + int output_surface_count;
> + VAStatus vas;
> + int i, err;
> +
> + av_vaapi_lock_hardware_context(hw_ctx);
> +
> + memset(ctx, 0, sizeof(*ctx));
> + ctx->class = &vaapi_pipeline_class;
> +
> + ctx->hardware_context = hw_ctx;
> +
> + if(pool) {
> + output_surface_count = pool->frame_count;
> + for(i = 0; i < output_surface_count; i++)
> + output_surface_ids[i] = vaapi_get_surface(pool->frames[i])->id;
> + } else {
> + // An output surface pool need not be supplied if the pipeline
> + // produces no image output (an intra-only codec like JPEG, say).
> +
> + output_surface_count = 0;
> + }
> +
> + vas = vaCreateConfig(hw_ctx->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;
> + }
> +
> + vas = vaCreateContext(hw_ctx->display, ctx->config_id,
> + config->width, config->height,
> + VA_PROGRESSIVE,
> + output_surface_ids, output_surface_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;
> + }
> +
> + av_log(ctx, AV_LOG_DEBUG, "VAAPI pipeline initialised: config %#x "
> + "context %#x.\n", ctx->config_id, ctx->context_id);
> +
> + err = 0;
> + fail:
> + av_vaapi_unlock_hardware_context(hw_ctx);
> + return err;
> +}
> +
> +int av_vaapi_pipeline_uninit(AVVAAPIPipelineContext *ctx)
> +{
> + VAStatus vas;
> +
> + av_vaapi_lock_hardware_context(ctx->hardware_context);
> +
> + av_assert0(ctx->hardware_context);
> +
> + vas = vaDestroyContext(ctx->hardware_context->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));
> + }
> +
> + vaDestroyConfig(ctx->hardware_context->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));
> + }
> +
> + av_vaapi_unlock_hardware_context(ctx->hardware_context);
> +
> + return 0;
> +}
> diff --git a/libavutil/vaapi.h b/libavutil/vaapi.h
> new file mode 100644
> index 0000000..53c4c7c
> --- /dev/null
> +++ b/libavutil/vaapi.h
> @@ -0,0 +1,115 @@
> +/*
> + * 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 AVVAAPIHardwareContext {
> + VADisplay display;
> +
> + VAConfigID decoder_pipeline_config_id;
> + VAContextID decoder_pipeline_context_id;
> +
> + void (*lock)(void *user_context);
> + void (*unlock)(void *user_context);
> + void *lock_user_context;
> +} AVVAAPIHardwareContext;
> +
> +AVVAAPIHardwareContext *av_vaapi_alloc_hardware_context(void);
> +
> +void av_vaapi_lock_hardware_context(AVVAAPIHardwareContext *ctx);
> +void av_vaapi_unlock_hardware_context(AVVAAPIHardwareContext *ctx);
> +
> +
> +#define AV_VAAPI_MAX_SURFACES 64
> +
> +typedef struct AVVAAPISurfaceConfig {
> + enum AVPixelFormat av_format;
> + unsigned int rt_format;
> + VAImageFormat image_format;
> +
> + unsigned int width;
> + unsigned int height;
> +
> + unsigned int attribute_count;
> + VASurfaceAttrib *attributes;
> +} AVVAAPISurfaceConfig;
> +
> +typedef struct AVVAAPISurfacePool {
> + AVVAAPIHardwareContext *hardware_context;
> +
> + int frame_count;
> + AVFrame *frames[AV_VAAPI_MAX_SURFACES];
> +} AVVAAPISurfacePool;
> +
> +int av_vaapi_surface_pool_init(AVVAAPISurfacePool *pool,
> + AVVAAPIHardwareContext *hw_ctx,
> + AVVAAPISurfaceConfig *config,
> + int frame_count);
> +
> +int av_vaapi_surface_pool_uninit(AVVAAPISurfacePool *pool);
> +
> +int av_vaapi_surface_pool_get(AVVAAPISurfacePool *pool, AVFrame *target);
> +
> +
> +typedef struct AVVAAPIPipelineConfig {
> + VAProfile profile;
> + VAEntrypoint entrypoint;
> +
> + unsigned int width;
> + unsigned int height;
> +
> + unsigned int attribute_count;
> + VAConfigAttrib *attributes;
> +} AVVAAPIPipelineConfig;
> +
> +typedef struct AVVAAPIPipelineContext {
> + const AVClass *class;
> +
> + AVVAAPIHardwareContext *hardware_context;
> +
> + VAConfigID config_id;
> + VAContextID context_id;
> +} AVVAAPIPipelineContext;
> +
> +int av_vaapi_pipeline_init(AVVAAPIPipelineContext *ctx,
> + AVVAAPIHardwareContext *hw_ctx,
> + AVVAAPIPipelineConfig *config,
> + AVVAAPISurfacePool *pool);
> +
> +int av_vaapi_pipeline_uninit(AVVAAPIPipelineContext *ctx);
> +
> +
> +int av_vaapi_map_frame(AVFrame *frame, int get);
> +int av_vaapi_unmap_frame(AVFrame *frame, int put);
> +
> +int av_vaapi_copy_to_surface(AVFrame *dst, const AVFrame *src);
> +int av_vaapi_copy_from_surface(AVFrame *dst, AVFrame *src);
It's still up to debate which of all this should be public API and what
not, I think.
If it gets to be public, we should probably take care of making it
future-proof. E.g. add alloc functions for structs, so we can add more
fields at a later point, without breaking ABI.
> +
> +
> +#endif /* LIBAVUTIL_VAAPI_H_ */
More information about the ffmpeg-devel
mailing list