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 libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel