Supports all surface formats that the OpenCL hwcontext does, with
mapping in both direction.
---
configure | 6 +
libavutil/hwcontext_internal.h | 3 +
libavutil/hwcontext_opencl.c | 421 +++++++++++++++++++++++++++++++++++++++++
libavutil/hwcontext_vaapi.c | 9 +
4 files changed, 439 insertions(+)
diff --git a/configure b/configure
index f8571da04..11dcaa336 100755
--- a/configure
+++ b/configure
@@ -1716,6 +1716,7 @@ HAVE_LIST="
dxva2_lib
libc_msvcrt
MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS
+ opencl_beignet_vaapi
sdl
section_data_rel_ro
threads
@@ -4855,6 +4856,11 @@ enabled vaapi &&
enabled vaapi &&
check_lib vaapi_x11 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 -lX11
+if enabled opencl && enabled vaapi ; then
+ check_type "CL/cl_intel.h" "clCreateImageFromFdINTEL_fn" &&
+ enable opencl_beignet_vaapi
+fi
+
enabled vdpau &&
check_cpp_condition vdpau/vdpau.h "defined
VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||
disable vdpau
diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
index ecda70558..2161d3162 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -164,4 +164,7 @@ extern const HWContextType ff_hwcontext_type_qsv;
extern const HWContextType ff_hwcontext_type_vaapi;
extern const HWContextType ff_hwcontext_type_vdpau;
+
+unsigned int ff_vaapi_fourcc_from_pix_fmt(enum AVPixelFormat pix_fmt);
+
#endif /* AVUTIL_HWCONTEXT_INTERNAL_H */
diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c
index 9a34a8c2b..b969770ce 100644
--- a/libavutil/hwcontext_opencl.c
+++ b/libavutil/hwcontext_opencl.c
@@ -29,6 +29,14 @@
#include "mem.h"
#include "pixdesc.h"
+#if HAVE_OPENCL_BEIGNET_VAAPI
+#include <unistd.h>
+#include <va/va.h>
+#include <va/va_drmcommon.h>
+#include <CL/cl_intel.h>
+#include "hwcontext_vaapi.h"
+#endif
+
typedef struct OpenCLDeviceContext {
// Internal command queue used for transfer/mapping operations
@@ -39,6 +47,12 @@ typedef struct OpenCLDeviceContext {
int default_frame_flags;
// Platform/device-specific functions.
+#if HAVE_OPENCL_BEIGNET_VAAPI
+ int vaapi_enabled;
+ clGetMemObjectFdIntel_fn clGetMemObjectFdIntel;
+ clCreateImageFromFdINTEL_fn clCreateImageFromFdINTEL;
+ clCreateBufferFromFdINTEL_fn clCreateBufferFromFdINTEL;
+#endif
} OpenCLDeviceContext;
@@ -406,6 +420,44 @@ static int opencl_device_init(AVHWDeviceContext *hwdev)
}
}
+#define CL_FUNC(name, desc) do { \
+ if (fail) \
+ break; \
+ ctx->name = clGetExtensionFunctionAddressForPlatform( \
+ hwctx->platform_id, #name); \
+ if (!ctx->name) { \
+ av_log(hwdev, AV_LOG_VERBOSE, \
+ desc " function not found (%s).\n", #name); \
+ fail = 1; \
+ } else { \
+ av_log(hwdev, AV_LOG_VERBOSE, \
+ desc " function found (%s).\n", #name); \
+ } \
+ } while (0)
+
+#if HAVE_OPENCL_BEIGNET_VAAPI
+ {
+ int fail = 0;
+
+ CL_FUNC(clGetMemObjectFdIntel,
+ "Intel OpenCL to DRM mapping");
+ CL_FUNC(clCreateBufferFromFdINTEL,
+ "Intel DRM to OpenCL buffer mapping");
+ CL_FUNC(clCreateImageFromFdINTEL,
+ "Intel DRM to OpenCL image mapping");
+
+ if (fail) {
+ av_log(hwdev, AV_LOG_WARNING, "VAAPI to OpenCL mapping "
+ "not usable.\n");
+ ctx->vaapi_enabled = 0;
+ } else {
+ ctx->vaapi_enabled = 1;
+ }
+ }
+#endif
+
+#undef CL_FUNC
+
return 0;
}
@@ -415,6 +467,36 @@ static int opencl_device_create(AVHWDeviceContext *ctx,
const char *device,
return opencl_device_create_internal(ctx, device, opts, flags, NULL);
}
+static int opencl_device_derive(AVHWDeviceContext *ctx,
+ AVHWDeviceContext *src_ctx,
+ int flags)
+{
+ int err;
+ AVDictionary *opts = NULL;
+ switch (src_ctx->type) {
+#if HAVE_OPENCL_BEIGNET_VAAPI
+ case AV_HWDEVICE_TYPE_VAAPI:
+ // There is no specific derivation function here because surface
+ // sharing just works via DRM fds and no special initialisation
+ // is required in advance. However, we do still need to find
+ // the Beignet ICD, so just create a reference to it by name.
+ err = av_dict_set(&opts, "platform_vendor", "Intel", 0);
+ if (err >= 0)
+ err = av_dict_set(&opts, "platform_version", "beignet", 0);
+ if (err >= 0)
+ err = opencl_device_create(ctx, ".0", opts, 0);
+ if (err >= 0)
+ err = opencl_device_init(ctx);
+ break;
+#endif
+ default:
+ err = AVERROR(ENOSYS);
+ break;
+ }
+ av_dict_free(&opts);
+ return err;
+}
+
static void opencl_device_uninit(AVHWDeviceContext *hwdev)
{
OpenCLDeviceContext *ctx = hwdev->internal->priv;
@@ -1129,11 +1211,345 @@ fail:
return err;
}
+
+#if HAVE_OPENCL_BEIGNET_VAAPI
+
+typedef struct VAAPItoOpenCLMapping {
+ VAImage va_image;
+ VABufferInfo va_buffer_info;
+
+ int nb_planes;
+ cl_mem plane[AV_NUM_DATA_POINTERS];
+} VAAPItoOpenCLMapping;
+
+static void opencl_unmap_from_vaapi(AVHWFramesContext *src_fc,
+ HWMapDescriptor *hwmap)
+{
+ VAAPItoOpenCLMapping *mapping = hwmap->priv;
+ AVVAAPIDeviceContext *src_dev = src_fc->device_ctx->hwctx;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ cl_int cle;
+ int i;
+
+ surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
+ av_log(src_fc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from OpenCL.\n",
+ surface_id);
+
+ for (i = 0; i < mapping->nb_planes; i++) {
+ cle = clReleaseMemObject(mapping->plane[i]);
+ if (cle != CL_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to release CL "
+ "buffer of plane %d of VA image %#x (derived "
+ "from surface %#x): %d.\n", i,
+ mapping->va_image.buf, surface_id, cle);
+ }
+ }
+
+ vas = vaReleaseBufferHandle(src_dev->display,
+ mapping->va_image.buf);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to release buffer "
+ "handle of image %#x (derived from surface %#x): "
+ "%d (%s).\n", mapping->va_image.buf, surface_id,
+ vas, vaErrorStr(vas));
+ }
+
+ vas = vaDestroyImage(src_dev->display,
+ mapping->va_image.image_id);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to destroy image "
+ "derived from surface %#x: %d (%s).\n",
+ surface_id, vas, vaErrorStr(vas));
+ }
+
+ av_free(mapping);
+}
+
+static int opencl_map_from_vaapi(AVHWFramesContext *dst_fc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ AVHWFramesContext *src_fc =
+ (AVHWFramesContext*)src->hw_frames_ctx->data;
+ AVVAAPIDeviceContext *src_dev = src_fc->device_ctx->hwctx;
+ AVOpenCLDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+ OpenCLDeviceContext *ctx = dst_fc->device_ctx->internal->priv;
+ VAAPItoOpenCLMapping *mapping = NULL;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ cl_int cle;
+ int err, i;
+
+ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
+ av_log(src_fc, AV_LOG_DEBUG, "Map VAAPI surface %#x to OpenCL.\n",
+ surface_id);
+
+ mapping = av_mallocz(sizeof(*mapping));
+ if (!mapping) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ vas = vaDeriveImage(src_dev->display, surface_id,
+ &mapping->va_image);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to derive image from "
+ "surface %#x: %d (%s).\n",
+ surface_id, vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ mapping->va_buffer_info.mem_type =
+ VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
+
+ vas = vaAcquireBufferHandle(src_dev->display,
+ mapping->va_image.buf,
+ &mapping->va_buffer_info);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to get buffer "
+ "handle from image %#x (derived from surface %#x): "
+ "%d (%s).\n", mapping->va_image.buf, surface_id,
+ vas, vaErrorStr(vas));
+ vaDestroyImage(src_dev->display, mapping->va_image.buf);
+ err = AVERROR(EIO);
+ goto fail_derived;
+ }
+
+ av_log(dst_fc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n",
+ mapping->va_buffer_info.handle);
+
+ mapping->nb_planes = mapping->va_image.num_planes;
+ for (i = 0; i < mapping->nb_planes; i++) {
+ cl_import_image_info_intel image_info = {
+ .fd = mapping->va_buffer_info.handle,
+ .size = mapping->va_buffer_info.mem_size,
+ .type = CL_MEM_OBJECT_IMAGE2D,
+ .offset = mapping->va_image.offsets[i],
+ .row_pitch = mapping->va_image.pitches[i],
+ };
+ cl_image_desc image_desc;
+
+ err = opencl_get_plane_format(src_fc->sw_format, i,
+ mapping->va_image.width,
+ mapping->va_image.height,
+ &image_info.fmt,
+ &image_desc);
+ if (err < 0) {
+ av_log(dst_fc, AV_LOG_ERROR, "VA %#x (derived from "
+ "surface %#x) has invalid parameters: %d.\n",
+ mapping->va_image.buf, surface_id, err);
+ goto fail_mapped;
+ }
+ image_info.width = image_desc.image_width;
+ image_info.height = image_desc.image_height;
+
+ mapping->plane[i] =
+ ctx->clCreateImageFromFdINTEL(dst_dev->context,
+ &image_info, &cle);
+ if (!mapping->plane[i]) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL image "
+ "from plane %d of VA image %#x (derived from "
+ "surface %#x): %d.\n", i,
+ mapping->va_image.buf, surface_id, cle);
+ err = AVERROR(EIO);
+ goto fail_mapped;
+ }
+
+ dst->data[i] = (uint8_t*)mapping->plane[i];
+ dst->linesize[i] = mapping->va_image.pitches[i];
+ }
+
+ err = ff_hwframe_map_create(src->hw_frames_ctx,
+ dst, src, &opencl_unmap_from_vaapi,
+ mapping);
+ if (err < 0)
+ goto fail_mapped;
+
+ dst->width = src->width;
+ dst->height = src->height;
+
+ return 0;
+
+fail_mapped:
+ for (i = 0; i < mapping->nb_planes; i++) {
+ if (mapping->plane[i])
+ clReleaseMemObject(mapping->plane[i]);
+ }
+ vaReleaseBufferHandle(src_dev->display, mapping->va_image.buf);
+fail_derived:
+ vaDestroyImage(src_dev->display, mapping->va_image.image_id);
+fail:
+ av_freep(&mapping);
+ return err;
+}
+
+typedef struct OpenCLtoVAAPIMapping {
+ int fd;
+
+ VASurfaceID surface_id;
+} OpenCLtoVAAPIMapping;
+
+static void opencl_unmap_to_vaapi(AVHWFramesContext *dst_fc,
+ HWMapDescriptor *hwmap)
+{
+ OpenCLtoVAAPIMapping *mapping = hwmap->priv;
+ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+
+ av_log(dst_fc, AV_LOG_VERBOSE, "Unmap fd %d / surface %#x.\n",
+ mapping->fd, mapping->surface_id);
+
+ vaDestroySurfaces(dst_dev->display, &mapping->surface_id, 1);
+
+ close(mapping->fd);
+
+ av_free(mapping);
+}
+
+static int opencl_map_to_vaapi(AVHWFramesContext *src_fc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ AVOpenCLDeviceContext *src_dev = src_fc->device_ctx->hwctx;
+ OpenCLDeviceContext *ctx = src_fc->device_ctx->internal->priv;
+ AVHWFramesContext *dst_fc =
+ (AVHWFramesContext*)dst->hw_frames_ctx->data;
+ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+ OpenCLtoVAAPIMapping *mapping = NULL;
+ AVOpenCLFrameDescriptor *desc;
+ VAStatus vas;
+ unsigned long va_fourcc;
+ cl_int cle;
+ unsigned long buffer_handle;
+ size_t offset;
+ int err, plane;
+
+ VASurfaceAttribExternalBuffers buffer_desc;
+ VASurfaceAttrib attrs[2] = {
+ {
+ .type = VASurfaceAttribMemoryType,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
+ },
+ {
+ .type = VASurfaceAttribExternalBufferDescriptor,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypePointer,
+ .value.value.p = &buffer_desc,
+ }
+ };
+
+ desc = (AVOpenCLFrameDescriptor*)src->buf[0]->data;
+ if (!(desc->flags & AV_OPENCL_FRAME_IMAGE_FROM_BUFFER)) {
+ av_log(src_fc, AV_LOG_ERROR, "Mapping OpenCL to VAAPI is only "
+ "possible on images created from single buffers.\n");
+ return AVERROR(EINVAL);
+ }
+
+ va_fourcc = ff_vaapi_fourcc_from_pix_fmt(src_fc->sw_format);
+ if (!va_fourcc) {
+ av_log(src_fc, AV_LOG_ERROR, "Unsupported format %d for "
+ "OpenCL to VAAPI mapping.\n", src_fc->sw_format);
+ goto fail;
+ }
+
+ mapping = av_mallocz(sizeof(*mapping));
+ if (!mapping) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ cle = ctx->clGetMemObjectFdIntel(src_dev->context,
+ desc->source_buffer,
+ &mapping->fd);
+ if (cle != CL_SUCCESS) {
+ av_log(src_fc, AV_LOG_ERROR, "Failed to get PRIME fd from "
+ "OpenCL buffer: %d.\n", cle);
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ av_log(dst_fc, AV_LOG_DEBUG, "DRM PRIME fd is %d.\n",
+ mapping->fd);
+
+ buffer_handle = mapping->fd;
+ buffer_desc.pixel_format = va_fourcc;
+ buffer_desc.width = src_fc->width;
+ buffer_desc.height = src_fc->height;
+ buffer_desc.buffers = &buffer_handle;
+ buffer_desc.num_buffers = 1;
+
+ offset = 0;
+ for (plane = 0;; plane++) {
+ cl_image_format image_format;
+ cl_image_desc image_desc;
+
+ err = opencl_get_plane_format(src_fc->sw_format, plane,
+ src_fc->width, src_fc->height,
+ &image_format, &image_desc);
+ if (err == AVERROR(ENOENT))
+ break;
+ if (err < 0)
+ goto fail_mapped;
+
+ buffer_desc.pitches[plane] = image_desc.image_row_pitch;
+ buffer_desc.offsets[plane] = offset;
+
+ offset += (image_desc.image_row_pitch *
+ image_desc.image_height);
+ }
+
+ buffer_desc.num_planes = plane;
+ buffer_desc.data_size = offset;
+
+ vas = vaCreateSurfaces(dst_dev->display,
+ VA_RT_FORMAT_YUV420,
+ src->width, src->height,
+ &mapping->surface_id, 1,
+ attrs, FF_ARRAY_ELEMS(attrs));
+ if(vas != VA_STATUS_SUCCESS) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to create surface from "
+ "DRM buffer: %d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail_mapped;
+ }
+
+ err = ff_hwframe_map_create(dst->hw_frames_ctx,
+ dst, src, &opencl_unmap_to_vaapi,
+ mapping);
+ if (err < 0)
+ goto fail_created;
+
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->data[3] = (uint8_t*)(uintptr_t)mapping->surface_id;
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM PRIME fd %d to "
+ "surface %#x.\n", mapping->fd, mapping->surface_id);
+
+ return 0;
+
+fail_created:
+ vaDestroySurfaces(dst_dev->display, &mapping->surface_id, 1);
+fail_mapped:
+ close(mapping->fd);
+fail:
+ av_freep(&mapping);
+ return err;
+}
+
+#endif
+
+
static int opencl_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
{
av_assert0(dst->format == AV_PIX_FMT_OPENCL);
switch (src->format) {
+#if HAVE_OPENCL_BEIGNET_VAAPI
+ case AV_PIX_FMT_VAAPI:
+ return opencl_map_from_vaapi(hwfc, dst, src, flags);
+#endif
default:
return AVERROR(ENOSYS);
}
@@ -1144,6 +1560,10 @@ static int opencl_map_from(AVHWFramesContext *hwfc,
AVFrame *dst,
{
av_assert0(src->format == AV_PIX_FMT_OPENCL);
switch (dst->format) {
+#if HAVE_OPENCL_BEIGNET_VAAPI
+ case AV_PIX_FMT_VAAPI:
+ return opencl_map_to_vaapi(hwfc, dst, src, flags);
+#endif
default:
if (hwfc->sw_format != dst->format)
return AVERROR(ENOSYS);
@@ -1160,6 +1580,7 @@ const HWContextType ff_hwcontext_type_opencl = {
.frames_hwctx_size = sizeof(AVOpenCLFramesContext),
.device_create = &opencl_device_create,
+ .device_derive = &opencl_device_derive,
.device_init = &opencl_device_init,
.device_uninit = &opencl_device_uninit,
diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 23bce7660..9f19d9d03 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -123,6 +123,15 @@ static enum AVPixelFormat
vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
return AV_PIX_FMT_NONE;
}
+unsigned int ff_vaapi_fourcc_from_pix_fmt(enum AVPixelFormat pix_fmt)
+{
+ 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)
--
2.11.0
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel