WIP. --- configure | 5 + libavutil/hwcontext_opencl.c | 354 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+)
diff --git a/configure b/configure index e601de5..1311612 100755 --- a/configure +++ b/configure @@ -1655,6 +1655,7 @@ HAVE_LIST=" libdc1394_1 libdc1394_2 MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS + intel_opencl_vaapi sdl section_data_rel_ro threads @@ -4735,6 +4736,10 @@ if enabled libcdio; then die "ERROR: No usable libcdio/cdparanoia found" fi +if enabled opencl ; then + check_type "CL/cl_intel.h" "clCreateImageFromFdINTEL_fn" && enable intel_opencl_vaapi +fi + check_lib X11/Xlib.h XOpenDisplay -lX11 && enable xlib if enabled libxcb || enabled x11grab && ! disabled libxcb; then diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c index 0a68afe..50de8fa 100644 --- a/libavutil/hwcontext_opencl.c +++ b/libavutil/hwcontext_opencl.c @@ -27,8 +27,20 @@ #include "hwcontext_opencl.h" #include "mem.h" +#if HAVE_INTEL_OPENCL_VAAPI +#include <unistd.h> +#include <CL/cl_intel.h> +#include "hwcontext_vaapi.h" +#include <va/va_drmcommon.h> +#endif + typedef struct OpenCLDeviceContext { +#if HAVE_INTEL_OPENCL_VAAPI + clGetMemObjectFdIntel_fn clGetMemObjectFdIntel; + clCreateImageFromFdINTEL_fn clCreateImageFromFdINTEL; + clCreateBufferFromFdINTEL_fn clCreateBufferFromFdINTEL; +#endif } OpenCLDeviceContext; static void opencl_error_callback(const char *errinfo, @@ -165,6 +177,43 @@ static int opencl_device_init(AVHWDeviceContext *hwdev) AVOpenCLDeviceContext *hwctx = hwdev->hwctx; OpenCLDeviceContext *ctx = hwdev->internal->priv; +#if HAVE_INTEL_OPENCL_VAAPI + // Check for the right extension before calling this... + + ctx->clGetMemObjectFdIntel = + clGetExtensionFunctionAddressForPlatform(hwctx->platform_id, + "clGetMemObjectFdIntel"); + if (!ctx->clGetMemObjectFdIntel) { + av_log(hwdev, AV_LOG_WARNING, + "Intel OpenCL to VAAPI mapping not found.\n"); + } else { + av_log(hwdev, AV_LOG_WARNING, + "Intel OpenCL to VAAPI mapping found.\n"); + } + + ctx->clCreateBufferFromFdINTEL = + clGetExtensionFunctionAddressForPlatform(hwctx->platform_id, + "clCreateBufferFromFdINTEL"); + if (!ctx->clCreateBufferFromFdINTEL) { + av_log(hwdev, AV_LOG_WARNING, + "Intel VAAPI to OpenCL mapping not found.\n"); + } else { + av_log(hwdev, AV_LOG_WARNING, + "Intel VAAPI to OpenCL mapping found.\n"); + } + + ctx->clCreateImageFromFdINTEL = + clGetExtensionFunctionAddressForPlatform(hwctx->platform_id, + "clCreateImageFromFdINTEL"); + if (!ctx->clCreateImageFromFdINTEL) { + av_log(hwdev, AV_LOG_WARNING, + "Intel VAAPI to OpenCL mapping not found.\n"); + } else { + av_log(hwdev, AV_LOG_WARNING, + "Intel VAAPI to OpenCL mapping found.\n"); + } +#endif + return 0; } @@ -311,6 +360,308 @@ static int opencl_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame) return 0; } +#if HAVE_INTEL_OPENCL_VAAPI + +typedef struct VAAPItoOpenCLMapping { + VAImage va_image; + VABufferInfo va_buffer_info; + + int nb_planes; + cl_mem plane[4]; +} VAAPItoOpenCLMapping; + +static void opencl_unmap_from_vaapi(AVHWFramesContext *src_fc, + FFHWMapDescriptor *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_malloc(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)); + return AVERROR(EIO); + } + + 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); + return AVERROR(EIO); + } + + 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 cl_image_info = { + .fd = mapping->va_buffer_info.handle, + .size = mapping->va_buffer_info.mem_size, + .type = CL_MEM_OBJECT_IMAGE2D, + .fmt = { + .image_channel_order = i ? CL_RG : CL_R, + .image_channel_data_type = CL_UNSIGNED_INT8, + }, + .offset = mapping->va_image.offsets[i], + .width = mapping->va_image.width / (i + 1), + .height = mapping->va_image.height / (i + 1), + .row_pitch = mapping->va_image.pitches[i], + }; + + mapping->plane[i] = + ctx->clCreateImageFromFdINTEL(dst_dev->context, + &cl_image_info, &cle); + if (!mapping->plane[i]) { + av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL buffer " + "from plane %d of VA image %#x (derived from " + "surface %#x): %d.\n", i, + mapping->va_image.buf, surface_id, cle); + return AVERROR(EIO); + } + + 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; + + dst->width = src->width; + dst->height = src->height; + + av_log(dst_fc, AV_LOG_DEBUG, "Mapped successfully!\n"); + + return 0; + +fail: + // TODO: cleanup. + return err; +} + +typedef struct OpenCLtoVAAPIMapping { + int fd; + + VASurfaceID surface_id; +} OpenCLtoVAAPIMapping; + +static void opencl_unmap_to_vaapi(AVHWFramesContext *dst_fc, + FFHWMapDescriptor *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; + VAStatus vas; + cl_int cle; + int err; + + mapping = av_malloc(sizeof(*mapping)); + if (!mapping) { + err = AVERROR(ENOMEM); + goto fail; + } + + cle = ctx->clGetMemObjectFdIntel(src_dev->context, + (cl_mem)src->data[0], + &mapping->fd); + if (cle != CL_SUCCESS) { + av_log(src_fc, AV_LOG_ERROR, "Failed to get PRIME fd from " + "CL buffer: %d.\n", cle); + return AVERROR(EIO); + } + + av_log(dst_fc, AV_LOG_DEBUG, "DRM PRIME fd is %d.\n", + mapping->fd); + + { + unsigned long buffer_handle = mapping->fd; + + VASurfaceAttribExternalBuffers buffer_desc = { + .pixel_format = VA_FOURCC_NV12, + .width = src_fc->width, + .height = src_fc->height, + .data_size = src_fc->width * src_fc->height * 3 / 2, + .num_planes = 2, + .pitches[0] = src_fc->width, + .offsets[0] = 0, + .pitches[1] = src_fc->width, + .offsets[1] = src_fc->width * src_fc->height, + .buffers = &buffer_handle, + .num_buffers = 1, + }; + + 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, + } + }; + + 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)); + return AVERROR(EIO); + } + } + + err = ff_hwframe_map_create(dst->hw_frames_ctx, + dst, src, &opencl_unmap_to_vaapi, + mapping); + if (err < 0) + goto fail; + + 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 successfully!\n"); + + return 0; + +fail: + // TODO: cleanup. + return err; +} + +#endif + +static int opencl_map_to(AVHWFramesContext *ctx, 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(ctx, dst, src, flags); +#endif + default: + return AVERROR(ENOSYS); + } +} + +static int opencl_map_from(AVHWFramesContext *ctx, AVFrame *dst, + const AVFrame *src, int flags) +{ + 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(ctx, dst, src, flags); +#endif + default: + return AVERROR(ENOSYS); + } +} + const HWContextType ff_hwcontext_type_opencl = { .type = AV_HWDEVICE_TYPE_OPENCL, .name = "OpenCL", @@ -326,6 +677,9 @@ const HWContextType ff_hwcontext_type_opencl = { .frames_uninit = &opencl_frames_uninit, .frames_get_buffer = &opencl_get_buffer, + .map_to = &opencl_map_to, + .map_from = &opencl_map_from, + .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_OPENCL, AV_PIX_FMT_NONE -- 2.8.1 _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel