WIP: working but very dirty.

Loads an arbitrary OpenCL kernel and runs it on the image.
---
 libavfilter/Makefile            |   1 +
 libavfilter/allfilters.c        |   1 +
 libavfilter/vf_opencl_inplace.c | 280 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 282 insertions(+)
 create mode 100644 libavfilter/vf_opencl_inplace.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 4ec244e..3bb6cc0 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -72,6 +72,7 @@ OBJS-$(CONFIG_NEGATE_FILTER)                 += vf_lut.o
 OBJS-$(CONFIG_NOFORMAT_FILTER)               += vf_format.o
 OBJS-$(CONFIG_NULL_FILTER)                   += vf_null.o
 OBJS-$(CONFIG_OCV_FILTER)                    += vf_libopencv.o
+OBJS-$(CONFIG_OPENCL_INPLACE_FILTER)         += vf_opencl_inplace.o
 OBJS-$(CONFIG_OVERLAY_FILTER)                += vf_overlay.o
 OBJS-$(CONFIG_PAD_FILTER)                    += vf_pad.o
 OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 36a484c..1bbe1bd 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -95,6 +95,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(NOFORMAT,       noformat,       vf);
     REGISTER_FILTER(NULL,           null,           vf);
     REGISTER_FILTER(OCV,            ocv,            vf);
+    REGISTER_FILTER(OPENCL_INPLACE, opencl_inplace, vf);
     REGISTER_FILTER(OVERLAY,        overlay,        vf);
     REGISTER_FILTER(PAD,            pad,            vf);
     REGISTER_FILTER(PIXDESCTEST,    pixdesctest,    vf);
diff --git a/libavfilter/vf_opencl_inplace.c b/libavfilter/vf_opencl_inplace.c
new file mode 100644
index 0000000..9a15a39
--- /dev/null
+++ b/libavfilter/vf_opencl_inplace.c
@@ -0,0 +1,280 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+
+#include "libavutil/buffer.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_opencl.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/opt.h"
+
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct OpenCLInplaceContext {
+    const AVClass   *class;
+
+    AVBufferRef     *device_ref;
+    AVOpenCLDeviceContext *hwctx;
+
+    cl_command_queue command_queue;
+    cl_program       program;
+    cl_kernel        kernel;
+
+    char            *program_file;
+    char            *kernel_name;
+    int              work_width;
+    int              work_height;
+} OpenCLInplaceContext;
+
+static int opencl_inplace_query_formats(AVFilterContext *avctx)
+{
+    const static enum AVPixelFormat formats[] = {
+        AV_PIX_FMT_OPENCL,
+        AV_PIX_FMT_NONE,
+    };
+
+    ff_formats_ref(ff_make_format_list(formats),
+                   &avctx->inputs[0]->out_formats);
+
+    ff_formats_ref(ff_make_format_list(formats),
+                   &avctx->outputs[0]->in_formats);
+
+    return 0;
+}
+
+static int opencl_inplace_load_program(AVFilterContext *avctx)
+{
+    OpenCLInplaceContext *ctx = avctx->priv;
+    cl_int cle;
+    FILE *source_file;
+    char *source;
+    size_t source_length;
+    int err;
+
+    ctx->command_queue = clCreateCommandQueue(ctx->hwctx->context,
+                                              ctx->hwctx->device_id,
+                                              0, &cle);
+    if (!ctx->command_queue) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to create OpenCL "
+               "command queue: %d.\n", cle);
+        err = AVERROR(ENOSYS);
+        goto fail;
+    }
+
+    // How to get this in properly?
+    source_file = fopen(ctx->program_file, "r");
+    source = malloc(1 << 20);
+    source_length = fread(source, 1, 1 << 20, source_file);
+    fclose(source_file);
+
+    ctx->program = clCreateProgramWithSource(ctx->hwctx->context, 1,
+                                             (const char**)&source,
+                                             &source_length, &cle);
+    if (!ctx->program) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to create program: %d.\n", cle);
+        err = AVERROR(EIO);
+        goto fail;
+    }
+
+    cle = clBuildProgram(ctx->program, 1, &ctx->hwctx->device_id,
+                         NULL, NULL, NULL);
+    if (cle != CL_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to build program: %d.\n", cle);
+
+        if (cle == CL_BUILD_PROGRAM_FAILURE) {
+            char *log;
+            size_t log_length;
+
+            clGetProgramBuildInfo(ctx->program, ctx->hwctx->device_id,
+                                  CL_PROGRAM_BUILD_LOG, 0, NULL, &log_length);
+
+            log = av_malloc(log_length);
+            if (log) {
+                cle = clGetProgramBuildInfo(ctx->program,
+                                            ctx->hwctx->device_id,
+                                            CL_PROGRAM_BUILD_LOG,
+                                            log_length, log, NULL);
+                if (cle == CL_SUCCESS)
+                    av_log(avctx, AV_LOG_ERROR, "Build log:\n%s\n", log);
+            }
+
+            av_free(log);
+        }
+
+        err = AVERROR(EIO);
+        goto fail;
+    }
+
+    ctx->kernel = clCreateKernel(ctx->program, ctx->kernel_name, &cle);
+    if (!ctx->kernel) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to create kernel: %d.\n", cle);
+        err = AVERROR(EIO);
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    return err;
+}
+
+static int opencl_inplace_filter_frame(AVFilterLink *inlink, AVFrame *pic)
+{
+    AVFilterContext    *avctx = inlink->dst;
+    AVFilterLink     *outlink = avctx->outputs[0];
+    OpenCLInplaceContext *ctx = avctx->priv;
+    AVHWFramesContext *hwfc;
+    cl_mem luma;
+    cl_int cle;
+    size_t global_work[2];
+    size_t local_work[2];
+    int err;
+
+    // Would be better if this could be done at init-time.
+    if (!pic->hw_frames_ctx)
+        return AVERROR(EINVAL);
+    hwfc = (AVHWFramesContext*)pic->hw_frames_ctx->data;
+    if (!hwfc || hwfc->format != AV_PIX_FMT_OPENCL)
+        return AVERROR(EINVAL);
+    ctx->hwctx = hwfc->device_ctx->hwctx;
+
+    if (!ctx->kernel) {
+        err = opencl_inplace_load_program(avctx);
+        if (err)
+            goto fail;
+    }
+
+    luma  = (cl_mem)pic->data[0];
+    av_log(avctx, AV_LOG_DEBUG, "Luma input is %p.\n", luma);
+
+    cle = clSetKernelArg(ctx->kernel, 0, sizeof(cl_mem), &luma);
+    if (cle != CL_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to set kernel argument: %d.\n", 
cle);
+        err = AVERROR(EIO);
+        goto fail;
+    }
+
+    global_work[0] = pic->width;
+    global_work[1] = pic->height;
+    local_work[0] = ctx->work_width;
+    local_work[1] = ctx->work_height;
+
+    av_log(avctx, AV_LOG_DEBUG, "Global %zux%zu, local %zux%zu.\n",
+           global_work[0], global_work[1],
+           local_work[0], local_work[1]);
+
+    cle = clEnqueueNDRangeKernel(ctx->command_queue, ctx->kernel, 2, NULL,
+                                 global_work, local_work, 0, NULL, NULL);
+    if (cle < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to enqueue kernel: %d.\n",
+               cle);
+        err = AVERROR(EIO);
+        goto fail;
+    }
+
+    cle = clFinish(ctx->command_queue);
+    if (cle != CL_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to finish command queue: %d.\n",
+               cle);
+        err = AVERROR(EIO);
+        goto fail;
+    }
+
+    return ff_filter_frame(outlink, pic);
+
+fail:
+    // Missing cleanup.
+    return err;
+}
+
+static av_cold void opencl_inplace_uninit(AVFilterContext *avctx)
+{
+    OpenCLInplaceContext *ctx = avctx->priv;
+    cl_int cle;
+
+    cle = clReleaseKernel(ctx->kernel);
+    if (cle != CL_SUCCESS)
+        av_log(avctx, AV_LOG_ERROR, "Failed to release kernel: %d.\n",
+               cle);
+
+    cle = clReleaseProgram(ctx->program);
+    if (cle != CL_SUCCESS)
+        av_log(avctx, AV_LOG_ERROR, "Failed to release program: %d.\n",
+               cle);
+
+    cle = clReleaseCommandQueue(ctx->command_queue);
+    if (cle != CL_SUCCESS)
+        av_log(avctx, AV_LOG_ERROR, "Failed to release command queue: %d.\n",
+               cle);
+}
+
+#define OFFSET(x) offsetof(OpenCLInplaceContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM)
+static const AVOption opencl_inplace_options[] = {
+    { "program", "OpenCL program source file",
+      OFFSET(program_file), AV_OPT_TYPE_STRING, { .str = "test.cl" }, .flags = 
FLAGS },
+    { "kernel", "Kernel name",
+      OFFSET(kernel_name), AV_OPT_TYPE_STRING, { .str = "test" }, .flags = 
FLAGS },
+    { "work_width", "Width of a kernel work element",
+      OFFSET(work_width),  AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = 
FLAGS },
+    { "work_height", "Height of a kernel work element",
+      OFFSET(work_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = 
FLAGS },
+    { NULL },
+};
+
+static const AVClass opencl_inplace_class = {
+    .class_name = "opencl_inplace",
+    .item_name  = av_default_item_name,
+    .option     = opencl_inplace_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVFilterPad opencl_inplace_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = &opencl_inplace_filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad opencl_inplace_outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_opencl_inplace = {
+    .name          = "opencl_inplace",
+    .description   = NULL_IF_CONFIG_SMALL("Do something unclear to an OpenCL 
frame"),
+    .priv_size     = sizeof(OpenCLInplaceContext),
+    .priv_class    = &opencl_inplace_class,
+    .uninit        = &opencl_inplace_uninit,
+    .query_formats = &opencl_inplace_query_formats,
+    .inputs        = opencl_inplace_inputs,
+    .outputs       = opencl_inplace_outputs,
+};
-- 
2.8.1

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

Reply via email to