Supports all surface formats that the OpenCL hwcontext does, with
mapping in both direction.
---
 configure                    |   6 +
 libavutil/hwcontext_opencl.c | 442 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 448 insertions(+)

diff --git a/configure b/configure
index 5b78131ab..a6933288b 100755
--- a/configure
+++ b/configure
@@ -1706,6 +1706,7 @@ HAVE_LIST="
     $TYPES_LIST
     dos_paths
     dxva2_lib
+    intel_opencl_vaapi
     libc_msvcrt
     MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS
     sdl
@@ -4822,6 +4823,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 intel_opencl_vaapi
+fi
+
 enabled vdpau &&
     check_cpp_condition vdpau/vdpau.h "defined 
VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||
     disable vdpau
diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c
index 0b108b7fc..7d14052b8 100644
--- a/libavutil/hwcontext_opencl.c
+++ b/libavutil/hwcontext_opencl.c
@@ -29,6 +29,14 @@
 #include "mem.h"
 #include "pixdesc.h"
 
+#if HAVE_INTEL_OPENCL_VAAPI
+#include <unistd.h>
+#include <va/va.h>
+#include <va/va_drmcommon.h>
+#include <CL/cl_intel.h>
+#include "hwcontext_vaapi.h"
+#endif
+
 // The maximum number of planes in an image.  This must be structly
 // less than AV_NUM_DATA_POINTERS because we place the whole-frame
 // reference in a buffer entry after the final plane.  For now, four
@@ -43,6 +51,12 @@ typedef struct OpenCLDeviceContext {
     cl_command_queue command_queue;
 
     // Platform/device-specific functions.
+#if HAVE_INTEL_OPENCL_VAAPI
+    int vaapi_enabled;
+    clGetMemObjectFdIntel_fn     clGetMemObjectFdIntel;
+    clCreateImageFromFdINTEL_fn  clCreateImageFromFdINTEL;
+    clCreateBufferFromFdINTEL_fn clCreateBufferFromFdINTEL;
+#endif
 } OpenCLDeviceContext;
 
 static void opencl_error_callback(const char *errinfo,
@@ -279,6 +293,40 @@ static int opencl_device_init(AVHWDeviceContext *hwdev)
         }
     }
 
+#define CL_FUNC(name, desc, failure) do {                       \
+        ctx->name = clGetExtensionFunctionAddressForPlatform(   \
+            hwctx->platform_id, #name);                         \
+        if (!ctx->name) {                                       \
+            av_log(hwdev, AV_LOG_VERBOSE,                       \
+                   desc " function not found (%s).\n", #name);  \
+            goto failure;                                       \
+        } else {                                                \
+            av_log(hwdev, AV_LOG_VERBOSE,                       \
+                   desc " function found (%s).\n", #name);      \
+        }                                                       \
+    } while (0)
+
+#if HAVE_INTEL_OPENCL_VAAPI
+    {
+        CL_FUNC(clGetMemObjectFdIntel,
+                "Intel OpenCL to DRM mapping", no_vaapi);
+        CL_FUNC(clCreateBufferFromFdINTEL,
+                "Intel DRM to OpenCL buffer mapping", no_vaapi);
+        CL_FUNC(clCreateImageFromFdINTEL,
+                "Intel DRM to OpenCL image mapping", no_vaapi);
+
+        ctx->vaapi_enabled = 1;
+        if (0) {
+        no_vaapi:
+            av_log(hwdev, AV_LOG_ERROR, "VAAPI to OpenCL mapping "
+                   "not usable.\n");
+            ctx->vaapi_enabled = 0;
+        }
+    }
+#endif
+
+#undef CL_FUNC
+
     return 0;
 }
 
@@ -288,6 +336,38 @@ 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_INTEL_OPENCL_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 = av_dict_set(&opts, "device_index", "0", 0);
+        if (err >= 0)
+            err = opencl_device_create(ctx, NULL, 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;
@@ -930,11 +1010,368 @@ fail:
     return err;
 }
 
+
+#if HAVE_INTEL_OPENCL_VAAPI
+
+typedef struct VAAPItoOpenCLMapping {
+    VAImage      va_image;
+    VABufferInfo va_buffer_info;
+
+    int       nb_planes;
+    cl_mem       plane[MAX_PLANES];
+} 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;
+    AVBufferRef           *obj_ref = NULL;
+    VAStatus vas;
+    unsigned long va_fourcc;
+    cl_int cle;
+    cl_mem obj;
+    unsigned long buffer_handle;
+    size_t offset;
+    int err, plane, nb_planes;
+
+    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,
+        }
+    };
+
+    // This must have been created by opencl_get_buffer() above, so we
+    // should have a data pointer for each plane with an associated
+    // buffer reference, and then a whole-object reference in the next
+    // buffer slot after that.
+    for (plane = 0; plane < MAX_PLANES; plane++) {
+        if (!src->data[plane])
+            break;
+        if (!src->buf[plane])
+            return AVERROR(EINVAL);
+    }
+    if (plane == MAX_PLANES)
+        return AVERROR(EINVAL);
+    obj_ref = src->buf[nb_planes = plane];
+    if (!obj_ref)
+        return AVERROR(EINVAL);
+    obj = (cl_mem)obj_ref->data;
+    if (!obj)
+        return AVERROR(EINVAL);
+
+    switch (src_fc->sw_format) {
+    case AV_PIX_FMT_YUV420P: va_fourcc = VA_FOURCC_IYUV; break;
+    case AV_PIX_FMT_NV12:    va_fourcc = VA_FOURCC_NV12; break;
+    case AV_PIX_FMT_P010:    va_fourcc = VA_FOURCC_P010; break;
+    case AV_PIX_FMT_RGBA:    va_fourcc = VA_FOURCC_RGBA; break;
+    case AV_PIX_FMT_BGRA:    va_fourcc = VA_FOURCC_BGRA; break;
+    case AV_PIX_FMT_ARGB:    va_fourcc = VA_FOURCC_ARGB; break;
+    case AV_PIX_FMT_ABGR:    va_fourcc = VA_FOURCC_ABGR; break;
+    default:
+        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, obj,
+                                     &mapping->fd);
+    if (cle != CL_SUCCESS) {
+        av_log(src_fc, AV_LOG_ERROR, "Failed to get PRIME fd from "
+               "CL 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);
+    }
+    if (plane != nb_planes) {
+        err = AVERROR(EINVAL);
+        goto fail_mapped;
+    }
+
+    buffer_desc.num_planes = nb_planes;
+    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_INTEL_OPENCL_VAAPI
+    case AV_PIX_FMT_VAAPI:
+        return opencl_map_from_vaapi(hwfc, dst, src, flags);
+#endif
     default:
         return AVERROR(ENOSYS);
     }
@@ -945,6 +1382,10 @@ static int opencl_map_from(AVHWFramesContext *hwfc, 
AVFrame *dst,
 {
     av_assert0(src->format == AV_PIX_FMT_OPENCL);
     switch (dst->format) {
+#if HAVE_INTEL_OPENCL_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);
@@ -961,6 +1402,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,
 
-- 
2.11.0

_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to