WIP: very incomplete. This just adds enough generic stuff to support the mapping in the following patch. --- configure | 6 + libavutil/Makefile | 2 + libavutil/hwcontext.c | 3 + libavutil/hwcontext.h | 1 + libavutil/hwcontext_internal.h | 1 + libavutil/hwcontext_opencl.c | 333 +++++++++++++++++++++++++++++++++++++++++ libavutil/hwcontext_opencl.h | 30 ++++ 7 files changed, 376 insertions(+) create mode 100644 libavutil/hwcontext_opencl.c create mode 100644 libavutil/hwcontext_opencl.h
diff --git a/configure b/configure index ce52f50..e601de5 100755 --- a/configure +++ b/configure @@ -240,6 +240,7 @@ External library support: --enable-nvenc Nvidia video encoding --enable-omx OpenMAX IL --enable-omx-rpi OpenMAX IL for Raspberry Pi + --enable-opencl OpenCL processing --enable-vaapi Video Acceleration API (mainly Unix/Intel) --enable-vda Apple Video Decode Acceleration [auto] --enable-vdpau Nvidia Video Decode and Presentation API for Unix [auto] @@ -1228,6 +1229,7 @@ HWACCEL_LIBRARY_LIST=" mmal nvenc omx + opencl vaapi vda vdpau @@ -4650,6 +4652,10 @@ enabled omx && { check_header OMX_Core.h || add_cflags -isystem/opt/vc/include/IL ; } check_header OMX_Core.h ; } || die "ERROR: OpenMAX IL headers not found"; } +enabled opencl && { { check_header CL/cl.h || + die "ERROR: OpenCL headers not found" ; } && + { check_lib CL/cl.h clGetPlatformIDs -lOpenCL || + die "ERROR: OpenCL ICD loader not found" ; } ; } enabled openssl && { check_pkg_config openssl openssl/ssl.h SSL_library_init && { add_cflags $openssl_cflags && add_extralibs $openssl_libs; }|| check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto || diff --git a/libavutil/Makefile b/libavutil/Makefile index b10b4d2..2d80d3b 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -111,6 +111,7 @@ OBJS = adler32.o \ OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o OBJS-$(CONFIG_DXVA2) += hwcontext_dxva2.o +OBJS-$(CONFIG_OPENCL) += hwcontext_opencl.o OBJS-$(CONFIG_LIBMFX) += hwcontext_qsv.o OBJS-$(CONFIG_LZO) += lzo.o OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o @@ -121,6 +122,7 @@ OBJS += $(COMPAT_OBJS:%=../compat/%) SKIPHEADERS-$(CONFIG_CUDA) += hwcontext_cuda.h SKIPHEADERS-$(CONFIG_DXVA2) += hwcontext_dxva2.h SKIPHEADERS-$(CONFIG_LIBMFX) += hwcontext_qsv.h +SKIPHEADERS-$(CONFIG_OPENCL) += hwcontext_opencl.h SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h SKIPHEADERS-$(HAVE_ATOMICS_GCC) += atomic_gcc.h diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index 5702ee1..19162aa 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -44,6 +44,9 @@ static const HWContextType *hw_table[] = { #if CONFIG_VDPAU &ff_hwcontext_type_vdpau, #endif +#if CONFIG_OPENCL + &ff_hwcontext_type_opencl, +#endif NULL, }; diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index 4473742..6f7c8ce 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -30,6 +30,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_DXVA2, AV_HWDEVICE_TYPE_QSV, + AV_HWDEVICE_TYPE_OPENCL, }; typedef struct AVHWDeviceInternal AVHWDeviceInternal; diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index 6aefca6..6e6697a 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -106,6 +106,7 @@ struct AVHWFramesInternal { extern const HWContextType ff_hwcontext_type_cuda; extern const HWContextType ff_hwcontext_type_dxva2; +extern const HWContextType ff_hwcontext_type_opencl; extern const HWContextType ff_hwcontext_type_qsv; extern const HWContextType ff_hwcontext_type_vaapi; extern const HWContextType ff_hwcontext_type_vdpau; diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c new file mode 100644 index 0000000..0a68afe --- /dev/null +++ b/libavutil/hwcontext_opencl.c @@ -0,0 +1,333 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> + +#include "config.h" + +#include "avassert.h" +#include "common.h" +#include "hwcontext.h" +#include "hwcontext_internal.h" +#include "hwcontext_opencl.h" +#include "mem.h" + + +typedef struct OpenCLDeviceContext { +} OpenCLDeviceContext; + +static void opencl_error_callback(const char *errinfo, + const void *private_info, size_t cb, + void *user_data) +{ + AVHWDeviceContext *ctx = user_data; + av_log(ctx, AV_LOG_ERROR, "OpenCL error: %s.", errinfo); +} + +static void opencl_device_free(AVHWDeviceContext *ctx) +{ + AVOpenCLDeviceContext *hwctx = ctx->hwctx; + cl_int cle; + + cle = clReleaseContext(hwctx->context); + if (cle != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to release OpenCL " + "context: %d.\n", cle); + } +} + +static int opencl_device_create(AVHWDeviceContext *ctx, const char *device, + AVDictionary *opts, int flags) +{ + cl_uint nb_platforms; + cl_platform_id *platforms = NULL; + cl_uint nb_devices; + cl_device_id *devices = NULL; + AVOpenCLDeviceContext *hwctx = ctx->hwctx; + cl_int cle; + int platform_idx, device_idx; + int ret; + + platform_idx = 0; + device_idx = 0; + if (device) { + platform_idx = strtol(device, NULL, 0); + if (strchr(device, ':')) + device_idx = strtol(strchr(device, ':') + 1, NULL, 0); + } + + cle = clGetPlatformIDs(0, NULL, &nb_platforms); + if (cle != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to get number of " + "OpenCL platforms: %d.\n", cle); + ret = AVERROR(ENOSYS); + goto fail; + } + if (nb_platforms < platform_idx) { + av_log(ctx, AV_LOG_ERROR, "Platform %d not found.\n", + platform_idx); + ret = AVERROR(ENOSYS); + goto fail; + } + av_log(ctx, AV_LOG_VERBOSE, "%d OpenCL platforms found.\n", + nb_platforms); + + platforms = av_malloc_array(nb_platforms, sizeof(*platforms)); + if (!platforms) { + ret = AVERROR(ENOMEM); + goto fail; + } + + cle = clGetPlatformIDs(nb_platforms, platforms, NULL); + if (cle != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to get list of OpenCL " + "platforms: %d.\n", cle); + ret = AVERROR(ENOSYS); + goto fail; + } + + cle = clGetDeviceIDs(platforms[platform_idx], + CL_DEVICE_TYPE_DEFAULT, + 0, NULL, &nb_devices); + if (cle != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to get number of " + "devices on platform %d: %d.\n", platform_idx, cle); + ret = AVERROR(ENOSYS); + goto fail; + } + if (nb_devices < device_idx) { + av_log(ctx, AV_LOG_ERROR, "Device %d not found on " + "platform %d.\n", device_idx, platform_idx); + ret = AVERROR(ENODEV); + goto fail; + } + av_log(ctx, AV_LOG_VERBOSE, "%d OpenCL devices found on " + "platform %d.\n", nb_devices, platform_idx); + + devices = av_malloc_array(nb_devices, sizeof(*devices)); + if (!devices) { + ret = AVERROR(ENOMEM); + goto fail; + } + + cle = clGetDeviceIDs(platforms[platform_idx], + CL_DEVICE_TYPE_DEFAULT, + nb_devices, devices, NULL); + if (cle != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to get list of devices " + "on platform %d: %d.\n", platform_idx, cle); + ret = AVERROR(ENODEV); + goto fail; + } + + hwctx->context = clCreateContext(NULL, 1, devices + device_idx, + &opencl_error_callback, ctx, + &cle); + if (!hwctx->context) { + av_log(ctx, AV_LOG_ERROR, "Failed to create OpenCL context " + "on platform %d device %d: %d.\n", + platform_idx, device_idx, cle); + ret = AVERROR(ENODEV); + goto fail; + } + + memcpy(&hwctx->platform_id, platforms + platform_idx, + sizeof(*platforms)); + memcpy(&hwctx->device_id, devices + device_idx, + sizeof(*devices)); + + ctx->free = &opencl_device_free; + + ret = 0; +fail: + av_freep(&platforms); + av_freep(&devices); + return ret; +} + +static int opencl_device_init(AVHWDeviceContext *hwdev) +{ + AVOpenCLDeviceContext *hwctx = hwdev->hwctx; + OpenCLDeviceContext *ctx = hwdev->internal->priv; + + return 0; +} + +static void opencl_device_uninit(AVHWDeviceContext *hwdev) +{ +} + +static void opencl_buffer_free(void *opaque, uint8_t *data) +{ + AVHWFramesContext *hwfc = opaque; + cl_int cle; + + cle = clReleaseMemObject((cl_mem)data); + if (cle != CL_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer: %d.\n", + cle); + } +} + +static AVBufferRef *opencl_pool_alloc(void *opaque, int size) +{ + AVHWFramesContext *hwfc = opaque; + AVOpenCLDeviceContext *hwctx = hwfc->device_ctx->hwctx; + cl_int cle; + cl_mem mem; + size_t total_size; + int i; + AVBufferRef *ref; + + // We allocate a buffer covering the whole image and then make + // sub-buffers for each plane. This makes sure that the image is + // one large object so that we can share with other APIs. + + // This only supports NV12 currently. + // TODO: make this more general by using the AVPixFmtDescriptor + // of hwfc->sw_format. + + total_size = 0; + for (i = 0; i < 2; i++) + total_size += hwfc->width * hwfc->height / (i + 1); + + mem = clCreateBuffer(hwctx->context, CL_MEM_READ_WRITE, + total_size, NULL, &cle); + if (!mem) { + av_log(hwfc, AV_LOG_ERROR, "Failed to allocate buffer " + "(%zu bytes): %d.\n", total_size, cle); + return NULL; + } + + ref = av_buffer_create((uint8_t*)mem, sizeof(cl_mem), + &opencl_buffer_free, hwfc, 0); + if (!ref) + return NULL; + + return ref; +} + +static int opencl_frames_init(AVHWFramesContext *hwfc) +{ + if (!hwfc->pool) { + hwfc->internal->pool_internal = + av_buffer_pool_init2(sizeof(cl_mem), hwfc, + &opencl_pool_alloc, NULL); + if (!hwfc->internal->pool_internal) + return AVERROR(ENOMEM); + } + + return 0; +} + +static void opencl_frames_uninit(AVHWFramesContext *hwfc) +{ +} + +static int opencl_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame) +{ + AVOpenCLDeviceContext *hwctx = hwfc->device_ctx->hwctx; + cl_mem mem, mem_plane[4], image_plane[4]; + cl_int cle; + size_t offset; + int i; + + frame->buf[2] = av_buffer_pool_get(hwfc->pool); + if (!frame->buf[2]) + return AVERROR(ENOMEM); + + mem = (cl_mem)frame->buf[2]->data; + + offset = 0; + for (i = 0; i < 2; i++) { + cl_buffer_region region = { + .origin = offset, + .size = (hwfc->width * hwfc->height) / (i + 1), + }; + cl_image_format image_format = { + .image_channel_order = i ? CL_RG : CL_R, + .image_channel_data_type = CL_UNSIGNED_INT8, + }; + cl_image_desc image_desc = { + .image_type = CL_MEM_OBJECT_IMAGE2D, + .image_width = hwfc->width / (i + 1), + .image_height = hwfc->height / (i + 1), + .image_row_pitch = hwfc->width, + }; + + mem_plane[i] = clCreateSubBuffer(mem, CL_MEM_READ_WRITE, + CL_BUFFER_CREATE_TYPE_REGION, + ®ion, &cle); + if (!mem_plane[i]) { + av_log(hwfc, AV_LOG_ERROR, "Failed to create sub-buffer " + "for plane %d: %d.\n", i, cle); + return AVERROR(EIO); + } + + image_desc.buffer = mem_plane[i]; + + image_plane[i] = clCreateImage(hwctx->context, + CL_MEM_READ_WRITE, + &image_format, + &image_desc, + NULL, &cle); + if (!image_plane[i]) { + av_log(hwfc, AV_LOG_ERROR, "Failed to create image from " + "plane %d sub-buffer: %d.\n", i, cle); + return AVERROR(EIO); + } + + offset += region.size; + frame->data[i] = (uint8_t*)image_plane[i]; + frame->linesize[i] = hwfc->width; + + frame->buf[i] = av_buffer_create((uint8_t*)image_plane[i], + sizeof(cl_mem), + &opencl_buffer_free, + hwfc, 0); + if (!frame->buf[i]) + return AVERROR(ENOMEM); + } + + frame->format = AV_PIX_FMT_OPENCL; + frame->width = hwfc->width; + frame->height = hwfc->height; + + return 0; +} + +const HWContextType ff_hwcontext_type_opencl = { + .type = AV_HWDEVICE_TYPE_OPENCL, + .name = "OpenCL", + + .device_hwctx_size = sizeof(AVOpenCLDeviceContext), + .device_priv_size = sizeof(OpenCLDeviceContext), + + .device_create = &opencl_device_create, + .device_init = &opencl_device_init, + .device_uninit = &opencl_device_uninit, + + .frames_init = &opencl_frames_init, + .frames_uninit = &opencl_frames_uninit, + .frames_get_buffer = &opencl_get_buffer, + + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_OPENCL, + AV_PIX_FMT_NONE + }, +}; diff --git a/libavutil/hwcontext_opencl.h b/libavutil/hwcontext_opencl.h new file mode 100644 index 0000000..655f933 --- /dev/null +++ b/libavutil/hwcontext_opencl.h @@ -0,0 +1,30 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_OPENCL_H +#define AVUTIL_HWCONTEXT_OPENCL_H + +#include <CL/cl.h> + +typedef struct AVOpenCLDeviceContext { + cl_platform_id platform_id; + cl_device_id device_id; + cl_context context; +} AVOpenCLDeviceContext; + +#endif /* AVUTIL_HWCONTEXT_OPENCL_H */ -- 2.8.1 _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel