Uses the cl_intel_va_api_media_sharing extension, which supports only
NV12 surfaces and only mapping from QSV to OpenCL.
---
configure | 5 +
libavutil/hwcontext_opencl.c | 262 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 267 insertions(+)
diff --git a/configure b/configure
index 11dcaa336..1ae720638 100755
--- a/configure
+++ b/configure
@@ -1717,6 +1717,7 @@ HAVE_LIST="
libc_msvcrt
MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS
opencl_beignet_vaapi
+ opencl_intel_media_vaapi
sdl
section_data_rel_ro
threads
@@ -4859,6 +4860,10 @@ enabled vaapi &&
if enabled opencl && enabled vaapi ; then
check_type "CL/cl_intel.h" "clCreateImageFromFdINTEL_fn" &&
enable opencl_beignet_vaapi
+ if enabled libmfx ; then
+ check_type "CL/cl.h CL/va_ext.h"
"clCreateFromVA_APIMediaSurfaceINTEL_fn" &&
+ enable opencl_intel_media_vaapi
+ fi
fi
enabled vdpau &&
diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c
index b969770ce..06dd4e95e 100644
--- a/libavutil/hwcontext_opencl.c
+++ b/libavutil/hwcontext_opencl.c
@@ -37,6 +37,13 @@
#include "hwcontext_vaapi.h"
#endif
+#if HAVE_OPENCL_INTEL_MEDIA_VAAPI
+#include <mfx/mfxstructures.h>
+#include <va/va.h>
+#include <CL/va_ext.h>
+#include "hwcontext_vaapi.h"
+#endif
+
typedef struct OpenCLDeviceContext {
// Internal command queue used for transfer/mapping operations
@@ -53,6 +60,16 @@ typedef struct OpenCLDeviceContext {
clCreateImageFromFdINTEL_fn clCreateImageFromFdINTEL;
clCreateBufferFromFdINTEL_fn clCreateBufferFromFdINTEL;
#endif
+
+#if HAVE_OPENCL_INTEL_MEDIA_VAAPI
+ int qsv_enabled;
+ clCreateFromVA_APIMediaSurfaceINTEL_fn
+ clCreateFromVA_APIMediaSurfaceINTEL;
+ clEnqueueAcquireVA_APIMediaSurfacesINTEL_fn
+ clEnqueueAcquireVA_APIMediaSurfacesINTEL;
+ clEnqueueReleaseVA_APIMediaSurfacesINTEL_fn
+ clEnqueueReleaseVA_APIMediaSurfacesINTEL;
+#endif
} OpenCLDeviceContext;
@@ -456,6 +473,85 @@ static int opencl_device_init(AVHWDeviceContext *hwdev)
}
#endif
+#if HAVE_OPENCL_INTEL_MEDIA_VAAPI
+ {
+ size_t props_size;
+ cl_context_properties *props = NULL;
+ VADisplay va_display;
+ const char *va_ext = "cl_intel_va_api_media_sharing";
+ int i, fail = 0;
+
+ if (!opencl_check_extension(hwdev, va_ext)) {
+ av_log(hwdev, AV_LOG_VERBOSE, "The %s extension is "
+ "required for QSV to OpenCL mapping.\n", va_ext);
+ goto no_qsv;
+ }
+
+ cle = clGetContextInfo(hwctx->context, CL_CONTEXT_PROPERTIES,
+ 0, NULL, &props_size);
+ if (cle != CL_SUCCESS) {
+ av_log(hwdev, AV_LOG_VERBOSE, "Failed to get context "
+ "properties: %d.\n", cle);
+ goto no_qsv;
+ }
+ if (props_size == 0) {
+ av_log(hwdev, AV_LOG_VERBOSE, "Media sharing must be "
+ "enabled on context creation to use QSV to "
+ "OpenCL mapping.\n");
+ goto no_qsv;
+ }
+
+ props = av_malloc(props_size);
+ if (!props)
+ return AVERROR(ENOMEM);
+
+ cle = clGetContextInfo(hwctx->context, CL_CONTEXT_PROPERTIES,
+ props_size, props, NULL);
+ if (cle != CL_SUCCESS) {
+ av_log(hwdev, AV_LOG_VERBOSE, "Failed to get context "
+ "properties: %d.\n", cle);
+ goto no_qsv;
+ }
+
+ va_display = NULL;
+ for (i = 0; i < (props_size / sizeof(*props) - 1); i++) {
+ if (props[i] == CL_CONTEXT_VA_API_DISPLAY_INTEL) {
+ va_display = (VADisplay)(intptr_t)props[i+1];
+ break;
+ }
+ }
+ if (!va_display) {
+ av_log(hwdev, AV_LOG_VERBOSE, "Media sharing must be "
+ "enabled on context creation to use QSV to "
+ "OpenCL mapping.\n");
+ goto no_qsv;
+ }
+ if (!vaDisplayIsValid(va_display)) {
+ av_log(hwdev, AV_LOG_VERBOSE, "A valid VADisplay is "
+ "required on context creation to use QSV to "
+ "OpenCL mapping.\n");
+ goto no_qsv;
+ }
+
+ CL_FUNC(clCreateFromVA_APIMediaSurfaceINTEL,
+ "Intel QSV to OpenCL mapping");
+ CL_FUNC(clEnqueueAcquireVA_APIMediaSurfacesINTEL,
+ "Intel QSV in OpenCL acquire");
+ CL_FUNC(clEnqueueReleaseVA_APIMediaSurfacesINTEL,
+ "Intel QSV in OpenCL release");
+
+ if (fail) {
+ no_qsv:
+ av_log(hwdev, AV_LOG_WARNING, "QSV to OpenCL mapping "
+ "not usable.\n");
+ ctx->qsv_enabled = 0;
+ } else {
+ ctx->qsv_enabled = 1;
+ }
+ av_free(props);
+ }
+#endif
+
#undef CL_FUNC
return 0;
@@ -489,6 +585,32 @@ static int opencl_device_derive(AVHWDeviceContext *ctx,
err = opencl_device_init(ctx);
break;
#endif
+#if HAVE_OPENCL_INTEL_MEDIA_VAAPI
+ // The generic code automatically attempts to derive from all
+ // ancestors of the given device, so we can ignore QSV devices
+ // here and just consider the inner device it was derived from.
+ case AV_HWDEVICE_TYPE_VAAPI:
+ {
+ AVVAAPIDeviceContext *src_hwctx = src_ctx->hwctx;
+ cl_context_properties props[7] = {
+ CL_CONTEXT_PLATFORM,
+ 0,
+ CL_CONTEXT_VA_API_DISPLAY_INTEL,
+ (intptr_t)src_hwctx->display,
+ CL_CONTEXT_INTEROP_USER_SYNC,
+ CL_FALSE,
+ 0,
+ };
+ err = av_dict_set(&opts, "device_extensions",
+ "cl_intel_va_api_media_sharing", 0);
+ if (err >= 0)
+ err = opencl_device_create_internal(ctx, NULL, opts,
+ 0, props);
+ if (err >= 0)
+ err = opencl_device_init(ctx);
+ }
+ break;
+#endif
default:
err = AVERROR(ENOSYS);
break;
@@ -1540,6 +1662,141 @@ fail:
#endif
+#if HAVE_OPENCL_INTEL_MEDIA_VAAPI
+
+typedef struct QSVtoOpenCLMapping {
+ int nb_planes;
+ cl_mem plane[AV_NUM_DATA_POINTERS];
+} QSVtoOpenCLMapping;
+
+static void opencl_unmap_from_qsv(AVHWFramesContext *dst_fc,
+ HWMapDescriptor *hwmap)
+{
+ QSVtoOpenCLMapping *mapping = hwmap->priv;
+ OpenCLDeviceContext *ctx = dst_fc->device_ctx->internal->priv;
+ cl_event event;
+ cl_int cle;
+ int i;
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Unmap QSV/VAAPI surface from OpenCL.\n");
+
+ cle = ctx->clEnqueueReleaseVA_APIMediaSurfacesINTEL(
+ ctx->command_queue, mapping->nb_planes, mapping->plane,
+ 0, NULL, &event);
+ if (cle != CL_SUCCESS) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to release surface "
+ "handle: %d.\n", cle);
+ }
+
+ opencl_wait_events(dst_fc, &event, 1);
+
+ for (i = 0; i < mapping->nb_planes; i++) {
+ cle = clReleaseMemObject(mapping->plane[i]);
+ if (cle != CL_SUCCESS) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to release CL "
+ "image of plane %d of QSV/VAAPI surface.\n", cle);
+ }
+ }
+
+ av_free(mapping);
+}
+
+static int opencl_map_from_qsv(AVHWFramesContext *dst_fc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ AVHWFramesContext *src_fc =
+ (AVHWFramesContext*)src->hw_frames_ctx->data;
+ AVOpenCLDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+ OpenCLDeviceContext *ctx = dst_fc->device_ctx->internal->priv;
+ QSVtoOpenCLMapping *mapping = NULL;
+ VASurfaceID va_surface;
+ cl_mem_flags cl_flags;
+ cl_event event;
+ cl_int cle;
+ int err, i;
+
+ if (src->format == AV_PIX_FMT_QSV) {
+ mfxFrameSurface1 *mfx_surface = (mfxFrameSurface1*)src->data[3];
+ va_surface = *(VASurfaceID*)mfx_surface->Data.MemId;
+ } else if (src->format == AV_PIX_FMT_VAAPI) {
+ va_surface = (VASurfaceID)(uintptr_t)src->data[3];
+ } else {
+ return AVERROR(ENOSYS);
+ }
+
+ if (flags & (AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE))
+ cl_flags = CL_MEM_READ_WRITE;
+ else if (flags & AV_HWFRAME_MAP_READ)
+ cl_flags = CL_MEM_READ_ONLY;
+ else if (flags & AV_HWFRAME_MAP_WRITE)
+ cl_flags = CL_MEM_WRITE_ONLY;
+ else
+ return AVERROR(EINVAL);
+
+ av_log(src_fc, AV_LOG_DEBUG, "Map QSV/VAAPI surface %#x to "
+ "OpenCL.\n", va_surface);
+
+ mapping = av_mallocz(sizeof(*mapping));
+ if (!mapping) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ // The cl_intel_va_api_media_sharing extension only supports NV12
+ // surfaces, so for now there are always exactly two planes.
+ mapping->nb_planes = 2;
+
+ for (i = 0; i < mapping->nb_planes; i++) {
+ mapping->plane[i] = ctx->clCreateFromVA_APIMediaSurfaceINTEL(
+ dst_dev->context, cl_flags, &va_surface, i, &cle);
+ if (!mapping->plane[i]) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to create CL "
+ "image from plane %d of QSV/VAAPI surface "
+ "%#x: %d.\n", i, va_surface, cle);
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ dst->data[i] = (uint8_t*)mapping->plane[i];
+ dst->linesize[i] = src_fc->width;
+ }
+
+ cle = ctx->clEnqueueAcquireVA_APIMediaSurfacesINTEL(
+ ctx->command_queue, mapping->nb_planes, mapping->plane,
+ 0, NULL, &event);
+ if (cle != CL_SUCCESS) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to acquire surface "
+ "handle: %d.\n", cle);
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ err = opencl_wait_events(dst_fc, &event, 1);
+ if (err < 0)
+ goto fail;
+
+ err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
+ &opencl_unmap_from_qsv, mapping);
+ if (err < 0)
+ goto fail;
+
+ dst->width = src->width;
+ dst->height = src->height;
+
+ return 0;
+
+fail:
+ if (mapping) {
+ for (i = 0; i < mapping->nb_planes; i++)
+ if (mapping->plane[i])
+ clReleaseMemObject(mapping->plane[i]);
+ }
+ av_freep(&mapping);
+ return err;
+}
+
+#endif
+
static int opencl_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
@@ -1550,6 +1807,11 @@ static int opencl_map_to(AVHWFramesContext *hwfc,
AVFrame *dst,
case AV_PIX_FMT_VAAPI:
return opencl_map_from_vaapi(hwfc, dst, src, flags);
#endif
+#if HAVE_OPENCL_INTEL_MEDIA_VAAPI
+ case AV_PIX_FMT_QSV:
+ case AV_PIX_FMT_VAAPI:
+ return opencl_map_from_qsv(hwfc, dst, src, flags);
+#endif
default:
return AVERROR(ENOSYS);
}
--
2.11.0
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel