On Sat, 30 Jan 2016 22:11:52 +0000 Mark Thompson <s...@jkqxz.net> wrote:
> --- > configure | 5 + > libavcodec/Makefile | 1 + > libavcodec/vaapi_support.c | 710 > +++++++++++++++++++++++++++++++++++++++++++++ > libavcodec/vaapi_support.h | 122 ++++++++ > 4 files changed, 838 insertions(+) > create mode 100644 libavcodec/vaapi_support.c > create mode 100644 libavcodec/vaapi_support.h > > diff --git a/configure b/configure > index dba8180..e7f53af 100755 > --- a/configure > +++ b/configure > @@ -2037,6 +2037,7 @@ CONFIG_EXTRA=" > texturedsp > texturedspenc > tpeldsp > + vaapi_recent > videodsp > vp3dsp > vp56dsp > @@ -5768,6 +5769,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/libavcodec/Makefile b/libavcodec/Makefile > index de957d8..045d118 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -719,6 +719,7 @@ OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcmenc.o > adpcm_data.o > OBJS-$(CONFIG_D3D11VA) += dxva2.o > OBJS-$(CONFIG_DXVA2) += dxva2.o > OBJS-$(CONFIG_VAAPI) += vaapi.o > +OBJS-$(CONFIG_VAAPI_RECENT) += vaapi_support.o > OBJS-$(CONFIG_VDA) += vda.o videotoolbox.o > OBJS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.o > OBJS-$(CONFIG_VDPAU) += vdpau.o > diff --git a/libavcodec/vaapi_support.c b/libavcodec/vaapi_support.c > new file mode 100644 > index 0000000..2e22f98 > --- /dev/null > +++ b/libavcodec/vaapi_support.c > @@ -0,0 +1,710 @@ > +/* > + * 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 "vaapi_support.h" > + > +#include "libavutil/avassert.h" > +#include "libavutil/imgutils.h" > +#include "libavutil/pixfmt.h" > + > + > +static AVClass vaapi_class = { > + .class_name = "vaapi", > + .item_name = av_default_item_name, > + .version = LIBAVUTIL_VERSION_INT, > +}; > +static AVClass *vaapi_log = &vaapi_class; We've talked about it on IRC. So the idea was matching AVVAAPIHardwareContext with vaapi_context, which is why the first member of AVVAAPIHardwareContext can't be AVClass. I think trying to somehow make these structs compatible should be given up, and instead the struct should have a vaapi_context pointer field instead. Also, I'm very worried about how this patch in general with combine with the API Libav is planning to add: https://lists.libav.org/pipermail/libav-devel/2016-January/074490.html To make this clear, above patch series: - extends the buffer pool to make it useful for the hwdec case (where you need to free the hw context when the final AVFrame is unreffed) - create a common API for hwaccel contexts, which overlaps with this patch to some degree, but not fully - adds a hwaccel context field to AVFrame - the hwaccel context is refcounted - adds helper for reading back data from hwaccel AVFrames in an API-independent way I'm not sure what to make out of this mess. Any ideas? I don't really want to hold this patch series back either. There is not much reason to refactor the whole API 1 week after it has been added either. > + > + > +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(&vaapi_log, AV_LOG_ERROR, "Failed to destroy surface: " > + "%d (%s).\n", vas, vaErrorStr(vas)); > + } > + > + 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; > + VASurfaceID surface_id; > + 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(&vaapi_log, AV_LOG_ERROR, "Failed to create surface: " > + "%d (%s).\n", vas, vaErrorStr(vas)); > + err = AVERROR_EXTERNAL; > + goto fail; > + } > + > + surface->id = surface_id; > + 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; > + } > + surface = 0; > + > + 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; > + frame = 0; > + } > + > + for(; i < FF_ARRAY_ELEMS(pool->frames); i++) > + pool->frames[i] = 0; > + > + av_buffer_unref(&config_buffer); > + > + av_log(&vaapi_log, AV_LOG_DEBUG, "Surface pool initialised: %u surfaces " > + "of %ux%u.\n", pool->frame_count, config->width, config->height); > + > + err = 0; > + if(0) { > + fail: This looks funny. My personal preference is avoiding such tricks, but maybe it's not important. > + if(surface) { > + if(surface->id) > + vaDestroySurfaces(hw_ctx->display, &surface->id, 1); > + av_free(surface); > + } > + if(frame) > + av_frame_free(&frame); > + for(--i; i >= 0; i--) > + av_frame_free(&pool->frames[i]); > + } > + > + 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]; > + break; > + } > + } > + > + if(frame) { > + target->data[3] = frame->data[3]; > + target->buf[0] = av_buffer_ref(frame->buf[0]); > + target->buf[1] = av_buffer_ref(frame->buf[1]); > + if(!target->buf[0] || !target->buf[1]) > + err = AVERROR(ENOMEM); > + else > + err = 0; > + } 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. > + int derive = 0; Before the patch gets applied, we also should check if we can get in a "GPU memcpy", and whether this codepath can be enabled. If not, there wouldn't be much of a reason to keep it. > + > + if(surface->mapped_address) { > + av_log(&vaapi_log, AV_LOG_ERROR, "Surface %#x already mapped.\n", > + surface->id); > + return AVERROR(EINVAL); > + } > + > + av_vaapi_lock_hardware_context(hw_ctx); > + > + vas = vaSyncSurface(hw_ctx->display, surface->id); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(&vaapi_log, 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(&vaapi_log, AV_LOG_VERBOSE, "Failed to derive image from " > + "surface %#x: %d (%s).\n", > + surface->id, vas, vaErrorStr(vas)); > + derive = 0; > + } > + } > + if(!derive) { > + vas = vaCreateImage(hw_ctx->display, &config->image_format, > + config->width, config->height, image); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(&vaapi_log, 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(&vaapi_log, AV_LOG_ERROR, "Failed to get image for " > + "surface %#x: %d (%s).\n", > + surface->id, vas, vaErrorStr(vas)); > + err = AVERROR_EXTERNAL; > + goto fail_image; > + } > + } > + } > + > + if(image->format.fourcc != config->image_format.fourcc) { > + av_log(&vaapi_log, AV_LOG_VERBOSE, "Returned image in unexpected " > + "format: asked for %#x, got %#x.\n", > + config->image_format.fourcc, image->format.fourcc); > + } > + > + vas = vaMapBuffer(hw_ctx->display, image->buf, &address); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(&vaapi_log, 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]; > + } > + > + if(image->format.fourcc == VA_FOURCC_YV12) { > + uint8_t *tmp; > + // Chroma planes are YVU rather than YUV, so swap them. > + tmp = frame->data[1]; > + frame->data[1] = frame->data[2]; > + frame->data[2] = tmp; > + } > + > + 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(&vaapi_log, 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; > +} > + > +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(&vaapi_log, 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(&vaapi_log, 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(&vaapi_log, AV_LOG_ERROR, "Failed to destroy image for > surface " > + "%#x: %d (%s).\n", surface->id, vas, vaErrorStr(vas)); > + } > + > + return 0; > +} > + > +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(&vaapi_log, 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->format = av_vaapi_pix_fmt(image->format.fourcc); > + dst->width = src->width; > + dst->height = src->height; > + > + av_frame_copy_props(dst, src); > + > + return dst; > +} > + > +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 < 0) > + 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 < 0) > + 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); > + > + av_frame_free(&proxy); > + > + return err; > +} > + > +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; > + int attr_count; > + VASurfaceAttrib *attr_list; > + int min_width, max_width, min_height, max_height; > + > + av_vaapi_lock_hardware_context(hw_ctx); > + > + memset(ctx, 0, sizeof(*ctx)); > + ctx->class = &vaapi_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_EXTERNAL; > + goto fail; > + } > + > + attr_count = 0; > + vas = vaQuerySurfaceAttributes(hw_ctx->display, ctx->config_id, > + 0, &attr_count); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(ctx, AV_LOG_ERROR, "Failed to get surface attribute count: " > + "%d (%s).\n", vas, vaErrorStr(vas)); > + err = AVERROR_EXTERNAL; > + goto fail_config; > + } > + > + attr_list = av_calloc(attr_count, sizeof(VASurfaceAttrib)); > + if(!attr_list) { > + err = AVERROR(ENOMEM); > + goto fail_config; > + } > + min_width = min_height = 0; // Min sizes need not be returned. > + max_width = max_height = INT_MIN; // Max sizes should be. > + > + vas = vaQuerySurfaceAttributes(hw_ctx->display, ctx->config_id, > + attr_list, &attr_count); > + if(vas != VA_STATUS_SUCCESS) > + attr_count = 0; > + for(i = 0; i < attr_count; i++) { > + switch(attr_list[i].type) { > + case VASurfaceAttribMinWidth: > + min_width = attr_list[i].value.value.i; > + break; > + case VASurfaceAttribMaxWidth: > + max_width = attr_list[i].value.value.i; > + break; > + case VASurfaceAttribMinHeight: > + min_height = attr_list[i].value.value.i; > + break; > + case VASurfaceAttribMaxHeight: > + max_height = attr_list[i].value.value.i; > + break; > + } > + } > + av_free(attr_list); > + if(attr_count == 0) { > + av_log(ctx, AV_LOG_ERROR, "Failed to get surface attributes: " > + "%d (%s).\n", vas, vaErrorStr(vas)); > + err = AVERROR_EXTERNAL; > + goto fail_config; > + } > + if(min_width > config->width || max_width < config->width || > + min_height > config->height || max_height < config->height) { > + av_log(ctx, AV_LOG_ERROR, "Pipeline does not support " > + "image size %dx%d (width %d-%d height %d-%d).\n", > + config->width, config->height, > + min_width, max_width, min_height, max_height); > + err = AVERROR(EINVAL); > + goto fail_config; > + } > + > + 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_EXTERNAL; > + goto fail_config; > + } > + > + av_log(ctx, AV_LOG_DEBUG, "VAAPI pipeline initialised: config %#x " > + "context %#x.\n", ctx->config_id, ctx->context_id); > + > + err = 0; > + if(0) { > + fail_config: > + vaDestroyConfig(hw_ctx->display, ctx->config_id); > + } > + 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; > +} > + > + > +static struct { > + unsigned int fourcc; > + enum AVPixelFormat pix_fmt; > +} vaapi_formats[] = { > +#define MAP(va, av) { VA_FOURCC_ ## va, AV_PIX_FMT_ ## av } > + MAP(NV12, NV12), > + MAP(IYUV, YUV420P), > + MAP(YV12, YUV420P), // With U/V planes swapped. > + MAP(YV16, YUV422P), > + MAP(UYVY, UYVY422), > + MAP(P010, P010), Heh, nice addition. > + MAP(Y800, GRAY8), > + MAP(BGRA, BGRA), > + MAP(BGRX, BGR0), > + MAP(RGBA, RGBA), > + MAP(RGBX, RGB0), > +#undef MAP > +}; > + > +enum AVPixelFormat av_vaapi_pix_fmt(unsigned int fourcc) > +{ > + int i; > + for(i = 0; i < FF_ARRAY_ELEMS(vaapi_formats); i++) > + if(vaapi_formats[i].fourcc == fourcc) > + return vaapi_formats[i].pix_fmt; > + return AV_PIX_FMT_NONE; > +} > + > +unsigned int av_vaapi_fourcc(enum AVPixelFormat pix_fmt) > +{ > + int i; > + for(i = 0; i < FF_ARRAY_ELEMS(vaapi_formats); i++) > + if(vaapi_formats[i].pix_fmt == pix_fmt) > + return vaapi_formats[i].fourcc; > + return 0; > +} > + > +int av_vaapi_get_image_format(AVVAAPIHardwareContext *hw_ctx, > + enum AVPixelFormat pix_fmt, > + VAImageFormat *image_format) > +{ > + VAStatus vas; > + VAImageFormat *format_list; > + int format_count, i, err; > + > + av_vaapi_lock_hardware_context(hw_ctx); > + > + format_count = vaMaxNumImageFormats(hw_ctx->display); > + if(format_count <= 0) { > + err = AVERROR_EXTERNAL; > + goto fail; > + } > + > + format_list = av_calloc(format_count, sizeof(VAImageFormat)); > + if(!format_list) { > + err = AVERROR(ENOMEM); > + goto fail; > + } > + > + vas = vaQueryImageFormats(hw_ctx->display, format_list, &format_count); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(&vaapi_log, AV_LOG_ERROR, "Failed to enumerate VAAPI image " > + "formats: %d (%s).\n", vas, vaErrorStr(vas)); > + err = AVERROR_EXTERNAL; > + goto fail; > + } > + > + for(i = 0; i < format_count; i++) { > + if(av_vaapi_pix_fmt(format_list[i].fourcc) == pix_fmt) { > + memcpy(image_format, &format_list[i], sizeof(VAImageFormat)); > + break; > + } > + } > + > + if(i < format_count) > + err = 0; > + else > + err = AVERROR(EINVAL); > + fail: > + av_vaapi_unlock_hardware_context(hw_ctx); > + return err; > +} > diff --git a/libavcodec/vaapi_support.h b/libavcodec/vaapi_support.h > new file mode 100644 > index 0000000..fb1c1b3 > --- /dev/null > +++ b/libavcodec/vaapi_support.h > @@ -0,0 +1,122 @@ > +/* > + * 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 "libavutil/pixfmt.h" > +#include "libavutil/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); > + > + > +enum AVPixelFormat av_vaapi_pix_fmt(unsigned int fourcc); > +unsigned int av_vaapi_fourcc(enum AVPixelFormat pix_fmt); > + > +int av_vaapi_get_image_format(AVVAAPIHardwareContext *hw_ctx, > + enum AVPixelFormat pix_fmt, > + VAImageFormat *image_format); > + > +#endif /* LIBAVUTIL_VAAPI_H_ */ Looks in general pretty ok to me. There should be allocation functions for structs which don't have one yet, and a clear warning should be added to the doxygen. (Doxygen should also be added.) One nit: I _think_ we usually add a whitespace between statement and "(", e.g. "if (". (Not for function calls or declarations.) _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel