Quoting Mark Thompson (2016-03-08 00:21:45)
> ---
> configure | 4 +
> libavutil/Makefile | 3 +
> libavutil/hwcontext.c | 3 +
> libavutil/hwcontext.h | 1 +
> libavutil/hwcontext_internal.h | 1 +
> libavutil/hwcontext_vaapi.c | 822
> +++++++++++++++++++++++++++++++++++++++++
> libavutil/hwcontext_vaapi.h | 70 ++++
> 7 files changed, 904 insertions(+)
> create mode 100644 libavutil/hwcontext_vaapi.c
> create mode 100644 libavutil/hwcontext_vaapi.h
>
> diff --git a/configure b/configure
> index d8b8c07..1ebcdd8 100755
> --- a/configure
> +++ b/configure
> @@ -4672,6 +4672,10 @@ if enabled x11grab; then
> require Xfixes X11/extensions/Xfixes.h XFixesGetCursorImage -lXfixes
> fi
>
> +enabled vaapi &&
> + check_code cc "va/va.h" "vaCreateSurfaces(0, 0, 0, 0, 0, 0, 0, 0)" ||
> + disable vaapi
> +
> 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 a095f0d..0956703 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -25,6 +25,7 @@ HEADERS = adler32.h
> \
> hmac.h \
> hwcontext.h \
> hwcontext_cuda.h \
> + hwcontext_vaapi.h \
> hwcontext_vdpau.h \
> imgutils.h \
> intfloat.h \
> @@ -108,11 +109,13 @@ OBJS = adler32.o
> \
>
> OBJS-$(CONFIG_LZO) += lzo.o
> OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o
> +OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o
> OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o
>
> OBJS += $(COMPAT_OBJS:%=../compat/%)
>
> SKIPHEADERS-$(CONFIG_CUDA) += hwcontext_cuda.h
> +SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h
> SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h
> SKIPHEADERS-$(HAVE_ATOMICS_GCC) += atomic_gcc.h
> SKIPHEADERS-$(HAVE_ATOMICS_SUNCC) += atomic_suncc.h
> diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
> index 53e11b8..ac1e2c9 100644
> --- a/libavutil/hwcontext.c
> +++ b/libavutil/hwcontext.c
> @@ -32,6 +32,9 @@ static const HWContextType *hw_table[] = {
> #if CONFIG_CUDA
> &ff_hwcontext_type_cuda,
> #endif
> +#if CONFIG_VAAPI
> + &ff_hwcontext_type_vaapi,
> +#endif
> #if CONFIG_VDPAU
> &ff_hwcontext_type_vdpau,
> #endif
> diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
> index 681b555..8502342 100644
> --- a/libavutil/hwcontext.h
> +++ b/libavutil/hwcontext.h
> @@ -27,6 +27,7 @@
> enum AVHWDeviceType {
> AV_HWDEVICE_TYPE_VDPAU,
> AV_HWDEVICE_TYPE_CUDA,
> + AV_HWDEVICE_TYPE_VAAPI,
> };
>
> typedef struct AVHWDeviceInternal AVHWDeviceInternal;
> diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
> index 27de1f9..b7828c0 100644
> --- a/libavutil/hwcontext_internal.h
> +++ b/libavutil/hwcontext_internal.h
> @@ -97,6 +97,7 @@ struct AVHWFramesInternal {
> };
>
> extern const HWContextType ff_hwcontext_type_cuda;
> +extern const HWContextType ff_hwcontext_type_vaapi;
> extern const HWContextType ff_hwcontext_type_vdpau;
>
> #endif /* AVUTIL_HWCONTEXT_INTERNAL_H */
> diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
> new file mode 100644
> index 0000000..18c4ad3
> --- /dev/null
> +++ b/libavutil/hwcontext_vaapi.c
> @@ -0,0 +1,822 @@
> +/*
> + * This file is part of Libav.
> + *
> + * Libav 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.
> + *
> + * Libav 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 Libav; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA
> + */
> +
> +#include "avassert.h"
> +#include "buffer.h"
> +#include "common.h"
> +#include "hwcontext.h"
> +#include "hwcontext_internal.h"
> +#include "hwcontext_vaapi.h"
> +#include "mem.h"
> +#include "pixdesc.h"
> +#include "pixfmt.h"
> +
> +typedef struct VAAPISurfaceFormat {
> + enum AVPixelFormat pix_fmt;
> + VAImageFormat image_format;
> +} VAAPISurfaceFormat;
> +
> +typedef struct VAAPIDeviceContext {
> + // Surface formats which can be used with this device.
> + VAAPISurfaceFormat *formats;
> + int nb_formats;
> +} VAAPIDeviceContext;
> +
> +typedef struct VAAPIFramesContext {
> + // Surface attributes set at create time.
> + VASurfaceAttrib *attributes;
> + int nb_attributes;
> + // RT format of the underlying surface (Intel driver ignores this
> anyway).
> + unsigned int rt_format;
> + // Whether vaDeriveImage works.
> + int derive_works;
> +} VAAPIFramesContext;
> +
> +enum {
> + VAAPI_MAP_READ = 0x01,
> + VAAPI_MAP_WRITE = 0x02,
> + VAAPI_MAP_DIRECT = 0x04,
> +};
> +
> +typedef struct VAAPISurfaceMap {
> + const AVFrame *source;
> + int flags;
> + VAImage image;
> +} VAAPISurfaceMap;
> +
> +#define MAP(va, rt, av) { \
> + VA_FOURCC_ ## va, \
> + VA_RT_FORMAT_ ## rt, \
> + AV_PIX_FMT_ ## av \
> + }
> +// The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
> +// plane swap cases. The frame handling below tries to hide these.
> +static struct {
> + unsigned int fourcc;
> + unsigned int rt_format;
> + enum AVPixelFormat pix_fmt;
> +} vaapi_format_map[] = {
> + MAP(NV12, YUV420, NV12),
> + MAP(YV12, YUV420, YUV420P), // With U/V planes swapped.
> + MAP(IYUV, YUV420, YUV420P),
> + //MAP(I420, YUV420, YUV420P), // Not in libva but used by Intel driver.
> +#ifdef VA_FOURCC_YV16
> + MAP(YV16, YUV422, YUV422P), // With U/V planes swapped.
> +#endif
> + MAP(422H, YUV422, YUV422P),
> + MAP(UYVY, YUV422, UYVY422),
> + MAP(YUY2, YUV422, YUYV422),
> + MAP(Y800, YUV400, GRAY8),
> + //MAP(P010, YUV420_10BPP, P010),
> + MAP(BGRA, RGB32, BGRA),
> + //MAP(BGRX, RGB32, BGR0),
> + MAP(RGBA, RGB32, RGBA),
> + //MAP(RGBX, RGB32, RGB0),
> + MAP(ABGR, RGB32, ABGR),
> + //MAP(XBGR, RGB32, 0BGR),
> + MAP(ARGB, RGB32, ARGB),
> + //MAP(XRGB, RGB32, 0RGB),
> +};
> +#undef MAP
> +
> +static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
> +{
> + int i;
> + for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
> + if (vaapi_format_map[i].fourcc == fourcc)
> + return vaapi_format_map[i].pix_fmt;
> + return AV_PIX_FMT_NONE;
> +}
> +
> +static unsigned int vaapi_fourcc_from_pix_fmt(enum AVPixelFormat pix_fmt)
This function seems to be unused.
> +{
> + int i;
> + for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
> + if (vaapi_format_map[i].pix_fmt == pix_fmt)
> + return vaapi_format_map[i].fourcc;
> + return 0;
> +}
> +
> +static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
> + enum AVPixelFormat pix_fmt,
> + VAImageFormat **image_format)
> +{
> + VAAPIDeviceContext *ctx = hwdev->internal->priv;
> + int i;
> +
> + for (i = 0; i < ctx->nb_formats; i++) {
> + if (ctx->formats[i].pix_fmt == pix_fmt) {
> + *image_format = &ctx->formats[i].image_format;
> + return 0;
> + }
> + }
> + return AVERROR(EINVAL);
> +}
> +
> +static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
> + const void *hwconfig,
> + AVHWFramesConstraints *constraints)
> +{
> + AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
> + const AVVAAPIHWConfig *config = hwconfig;
> + AVVAAPIHWConfig *tmp_config;
> + VASurfaceAttrib *attr_list = NULL;
> + VAStatus vas;
> + enum AVPixelFormat pix_fmt;
> + unsigned int fourcc;
> + int err, i, j, attr_count, pix_fmt_count;
> +
> + if (!hwconfig) {
> + // No configuration was provided, so we create a temporary pipeline
> + // configuration in order to query all supported image formats.
> +
> + tmp_config = av_mallocz(sizeof(*config));
> + if (!tmp_config)
> + return AVERROR(ENOMEM);
> +
> + vas = vaCreateConfig(hwctx->display,
> + VAProfileNone, VAEntrypointVideoProc,
> + 0, 0, &tmp_config->config_id);
> + if (vas != VA_STATUS_SUCCESS) {
> + // No vpp. We might still be able to do something useful if
> + // codecs are supported, so try to make the most-commonly
> + // supported decoder configuration we can to query instead.
> + vas = vaCreateConfig(hwctx->display,
> + VAProfileH264ConstrainedBaseline,
> + VAEntrypointVLD, 0, 0,
> + &tmp_config->config_id);
> + if (vas != VA_STATUS_SUCCESS)
> + return AVERROR(ENOSYS);
leaking tmp_config here
Also, one of those zeros should be NULL I think, some compilers might
complain about that.
> + }
> +
> + config = tmp_config;
> + }
> +
> + attr_count = 0;
> + vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
> + 0, &attr_count);
> + if (vas != VA_STATUS_SUCCESS) {
> + av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
> + "%d (%s).\n", vas, vaErrorStr(vas));
> + err = AVERROR(ENOSYS);
> + goto fail;
> + }
> +
> + attr_list = av_malloc(attr_count * sizeof(*attr_list));
> + if (!attr_list) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
> + attr_list, &attr_count);
> + if (vas != VA_STATUS_SUCCESS) {
> + av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
> + "%d (%s).\n", vas, vaErrorStr(vas));
> + err = AVERROR(ENOSYS);
> + goto fail;
> + }
> +
> + pix_fmt_count = 0;
> + for (i = 0; i < attr_count; i++) {
> + switch (attr_list[i].type) {
> + case VASurfaceAttribPixelFormat:
> + fourcc = attr_list[i].value.value.i;
> + pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
> + if (pix_fmt != AV_PIX_FMT_NONE) {
> + ++pix_fmt_count;
> + } else {
> + // Something unsupported - ignore.
> + }
> + break;
> + case VASurfaceAttribMinWidth:
> + constraints->min_width = attr_list[i].value.value.i;
> + break;
> + case VASurfaceAttribMinHeight:
> + constraints->min_height = attr_list[i].value.value.i;
> + break;
> + case VASurfaceAttribMaxWidth:
> + constraints->max_width = attr_list[i].value.value.i;
> + break;
> + case VASurfaceAttribMaxHeight:
> + constraints->max_height = attr_list[i].value.value.i;
> + break;
> + }
> + }
> + if (pix_fmt_count == 0) {
> + // Nothing usable found. Presumably there exists something which
> + // works, so leave the set null to indicate unknown.
> + constraints->valid_sw_formats = NULL;
> + } else {
> + constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
> + sizeof(pix_fmt));
> + if (!constraints->valid_sw_formats) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + for (i = j = 0; i < attr_count; i++) {
> + if (attr_list[i].type != VASurfaceAttribPixelFormat)
> + continue;
> + fourcc = attr_list[i].value.value.i;
> + pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
> + if (pix_fmt != AV_PIX_FMT_NONE)
> + constraints->valid_sw_formats[j++] = pix_fmt;
> + }
> + av_assert0(j == pix_fmt_count);
> + constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
> + }
> +
> + constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
> + if (!constraints->valid_hw_formats) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> + constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
> + constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
> +
> + av_freep(&attr_list);
> + if (!hwconfig) {
> + vaDestroyConfig(hwctx->display, tmp_config->config_id);
> + av_freep(&tmp_config);
> + }
> +
> + return 0;
> +
> + fail:
> + av_freep(constraints->valid_sw_formats);
I don't think you need to free this. Especially as you don't free the
formats either.
> + av_freep(&attr_list);
> + if (!hwconfig) {
> + vaDestroyConfig(hwctx->display, tmp_config->config_id);
> + av_freep(&tmp_config);
> + }
> + return err;
> +}
> +
> +static int vaapi_device_init(AVHWDeviceContext *hwdev)
> +{
> + VAAPIDeviceContext *ctx = hwdev->internal->priv;
> + AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
> + AVHWFramesConstraints *constraints = NULL;
> + VAImageFormat *image_list = NULL;
> + VAStatus vas;
> + int err, i, j, image_count;
> + enum AVPixelFormat pix_fmt;
> + unsigned int fourcc;
> +
> + constraints = av_mallocz(sizeof(*constraints));
> + if (!constraints)
> + goto fail;
> +
> + err = vaapi_frames_get_constraints(hwdev, NULL, constraints);
> + if (err < 0)
> + goto fail;
> +
> + image_count = vaMaxNumImageFormats(hwctx->display);
> + if (image_count <= 0) {
> + err = AVERROR(EIO);
> + goto fail;
> + }
> + image_list = av_malloc(image_count * sizeof(*image_list));
> + if (!image_list) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> + vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
> + if (vas != VA_STATUS_SUCCESS) {
> + err = AVERROR(EIO);
> + goto fail;
> + }
> +
> + ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
> + if (!ctx->formats) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> + ctx->nb_formats = 0;
> + for (i = 0; i < image_count; i++) {
> + fourcc = image_list[i].fourcc;
> + pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
> + for (j = 0; constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE;
> j++) {
> + if (pix_fmt == constraints->valid_sw_formats[j])
> + break;
> + }
> + if (constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE) {
> + av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
> + fourcc, av_get_pix_fmt_name(pix_fmt));
> + ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
> + ctx->formats[ctx->nb_formats].image_format = image_list[i];
> + ++ctx->nb_formats;
> + } else {
> + av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n", fourcc);
> + }
> + }
> +
> + av_free(image_list);
> + av_hwframe_constraints_free(&constraints);
> + return 0;
> + fail:
> + av_freep(&ctx->formats);
> + av_free(image_list);
> + av_hwframe_constraints_free(&constraints);
> + return err;
> +}
> +
> +static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
> +{
> + VAAPIDeviceContext *ctx = hwdev->internal->priv;
> +
> + av_freep(&ctx->formats);
> +}
> +
> +static void vaapi_buffer_free(void *opaque, uint8_t *data)
> +{
> + AVHWFramesContext *hwfc = opaque;
> + AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
> + VASurfaceID surface_id;
> + VAStatus vas;
> +
> + surface_id = (VASurfaceID)(uintptr_t)data;
> +
> + vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
> + if (vas != VA_STATUS_SUCCESS) {
> + av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
> + "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
> + }
> +}
> +
> +static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
> +{
> + AVHWFramesContext *hwfc = opaque;
> + VAAPIFramesContext *ctx = hwfc->internal->priv;
> + AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
> + AVVAAPIFramesContext *avfc = hwfc->hwctx;
> + VASurfaceID surface_id;
> + VAStatus vas;
> + AVBufferRef *ref;
> +
> + vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
> + hwfc->width, hwfc->height,
> + &surface_id, 1,
> + ctx->attributes, ctx->nb_attributes);
> + if (vas != VA_STATUS_SUCCESS) {
> + av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
> + "%d (%s).\n", vas, vaErrorStr(vas));
> + return NULL;
> + }
> + av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
> +
> + ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
> + sizeof(surface_id), &vaapi_buffer_free,
> + hwfc, AV_BUFFER_FLAG_READONLY);
> + if (!ref) {
> + vaDestroySurfaces(hwctx->display, &surface_id, 1);
> + return NULL;
> + }
> +
> + if (hwfc->initial_pool_size > 0) {
> + av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
> + avfc->surface_ids[avfc->nb_surfaces] = surface_id;
> + }
> + ++avfc->nb_surfaces;
> + if (avfc->nb_surfaces == 1) {
> + // Test whether vaDeriveImage() works for this type of surface.
> + VAImageFormat *expected_format;
> + VAImage test_image;
> + int err;
> +
> + ctx->derive_works = 0;
I think this is unsafe, since this variable is also accessed in
mapping the frame, which can easily happen concurrently with this
function. So it would be better to make this test in frames_init.
Also, this function won't be called at all if the caller supplies his
own frame pool. The mapping function should work even then.
--
Anton Khirnov
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel