diff mbox

[FFmpeg-devel] lavfi: add lumakey_opencl filter

Message ID 1532520816-4990-1-git-send-email-danyaschenko@gmail.com
State New
Headers show

Commit Message

Danil Iashchenko July 25, 2018, 12:13 p.m. UTC
Add lumakey_opencl filter. Behaves like existing lumakey filter.

---
 configure                       |   1 +
 libavfilter/Makefile            |   2 +
 libavfilter/allfilters.c        |   1 +
 libavfilter/opencl/lumakey.cl   |  43 +++++++
 libavfilter/opencl_source.h     |   1 +
 libavfilter/vf_lumakey_opencl.c | 243 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 291 insertions(+)
 create mode 100644 libavfilter/opencl/lumakey.cl
 create mode 100644 libavfilter/vf_lumakey_opencl.c

Comments

James Almer July 25, 2018, 1:50 p.m. UTC | #1
On 7/25/2018 9:13 AM, Danil Iashchenko wrote:
> Add lumakey_opencl filter. Behaves like existing lumakey filter.

Isn't it possible to keep each of these new OpenCL filters as an
optional codepath within the C version, using an AVOption like "opencl"
or "hwaccel" to toggle one or another? Or maybe autodetected depending
on the filter chain and/or input pix_fmt?

I'm asking because it's getting too crowded. We also have some vaapi and
qsv duplicate filters, and once we start committing filters using the
upcoming Vulkan hwcontext the same way, we may also end up introducing
yet another hardware specific variant for each of these.

In libavcodec the hwaccels are seamlessly integrated into supported
decoders. This has been favored over separate full stream hardware
decoders where possible for the above reasons. It would be ideal to
achieve the same with libavfilter.
Michael Niedermayer July 25, 2018, 10:17 p.m. UTC | #2
On Wed, Jul 25, 2018 at 10:50:43AM -0300, James Almer wrote:
> On 7/25/2018 9:13 AM, Danil Iashchenko wrote:
> > Add lumakey_opencl filter. Behaves like existing lumakey filter.
> 
> Isn't it possible to keep each of these new OpenCL filters as an
> optional codepath within the C version, using an AVOption like "opencl"
> or "hwaccel" to toggle one or another? Or maybe autodetected depending
> on the filter chain and/or input pix_fmt?
> 
> I'm asking because it's getting too crowded. We also have some vaapi and
> qsv duplicate filters, and once we start committing filters using the
> upcoming Vulkan hwcontext the same way, we may also end up introducing
> yet another hardware specific variant for each of these.
> 

> In libavcodec the hwaccels are seamlessly integrated into supported
> decoders. This has been favored over separate full stream hardware
> decoders where possible for the above reasons. It would be ideal to
> achieve the same with libavfilter.

i am in favor of this design as well. The user should not need to have
to know about and manage manually GPU optimizations.

thx

[...]
Danil Iashchenko July 27, 2018, 10:22 a.m. UTC | #3
Add lumakey_opencl filter. Behaves like existing lumakey filter.

---
 
On Wed, Jul 25, 2018 at 10:50:43AM -0300, James Almer wrote:
>> On 7/25/2018 9:13 AM, Danil Iashchenko wrote:
>> > Add lumakey_opencl filter. Behaves like existing lumakey filter.
>> 
>> Isn't it possible to keep each of these new OpenCL filters as an
>> optional codepath within the C version, using an AVOption like "opencl"
>> or "hwaccel" to toggle one or another? Or maybe autodetected depending
>> on the filter chain and/or input pix_fmt?
>> 
>> I'm asking because it's getting too crowded. We also have some vaapi and
>> qsv duplicate filters, and once we start committing filters using the
>> upcoming Vulkan hwcontext the same way, we may also end up introducing
>> yet another hardware specific variant for each of these.
>> 

>> In libavcodec the hwaccels are seamlessly integrated into supported
>> decoders. This has been favored over separate full stream hardware
>> decoders where possible for the above reasons. It would be ideal to
>> achieve the same with libavfilter.

>i am in favor of this design as well. The user should not need to have
>to know about and manage manually GPU optimizations.

>thx

Hi! I am GSoC student and I still have some tasks before the program ends. 
Also my mentor said:
 <jkqxz> IMO don't think about it now, there isn't that much time left.
 <jkqxz> I looked at doing last year (when converting to the current structure) and concluded that it's not really sane to do.
 <jkqxz> The _opencl versions of filters operate completely differently, so while some code for setup can be shared putting them in the same filter doesn't really make sense.

Thanks, Danil.
James Almer July 27, 2018, 2:42 p.m. UTC | #4
On 7/27/2018 7:22 AM, Danil Iashchenko wrote:
> Add lumakey_opencl filter. Behaves like existing lumakey filter.
> 
> ---
>  
> On Wed, Jul 25, 2018 at 10:50:43AM -0300, James Almer wrote:
>>> On 7/25/2018 9:13 AM, Danil Iashchenko wrote:
>>>> Add lumakey_opencl filter. Behaves like existing lumakey filter.
>>>
>>> Isn't it possible to keep each of these new OpenCL filters as an
>>> optional codepath within the C version, using an AVOption like "opencl"
>>> or "hwaccel" to toggle one or another? Or maybe autodetected depending
>>> on the filter chain and/or input pix_fmt?
>>>
>>> I'm asking because it's getting too crowded. We also have some vaapi and
>>> qsv duplicate filters, and once we start committing filters using the
>>> upcoming Vulkan hwcontext the same way, we may also end up introducing
>>> yet another hardware specific variant for each of these.
>>>
> 
>>> In libavcodec the hwaccels are seamlessly integrated into supported
>>> decoders. This has been favored over separate full stream hardware
>>> decoders where possible for the above reasons. It would be ideal to
>>> achieve the same with libavfilter.
> 
>> i am in favor of this design as well. The user should not need to have
>> to know about and manage manually GPU optimizations.
> 
>> thx
> 
> Hi! I am GSoC student and I still have some tasks before the program ends. 
> Also my mentor said:
>  <jkqxz> IMO don't think about it now, there isn't that much time left.
>  <jkqxz> I looked at doing last year (when converting to the current structure) and concluded that it's not really sane to do.
>  <jkqxz> The _opencl versions of filters operate completely differently, so while some code for setup can be shared putting them in the same filter doesn't really make sense.
> 
> Thanks, Danil.

Fair enough, i wasn't aware this was for GSoC, and this kind of change
(assuming it's doable) would definitely take a while, so it can wait.
Michael Niedermayer July 28, 2018, 1:03 a.m. UTC | #5
On Fri, Jul 27, 2018 at 11:42:57AM -0300, James Almer wrote:
> On 7/27/2018 7:22 AM, Danil Iashchenko wrote:
> > Add lumakey_opencl filter. Behaves like existing lumakey filter.
> > 
> > ---
> >  
> > On Wed, Jul 25, 2018 at 10:50:43AM -0300, James Almer wrote:
> >>> On 7/25/2018 9:13 AM, Danil Iashchenko wrote:
> >>>> Add lumakey_opencl filter. Behaves like existing lumakey filter.
> >>>
> >>> Isn't it possible to keep each of these new OpenCL filters as an
> >>> optional codepath within the C version, using an AVOption like "opencl"
> >>> or "hwaccel" to toggle one or another? Or maybe autodetected depending
> >>> on the filter chain and/or input pix_fmt?
> >>>
> >>> I'm asking because it's getting too crowded. We also have some vaapi and
> >>> qsv duplicate filters, and once we start committing filters using the
> >>> upcoming Vulkan hwcontext the same way, we may also end up introducing
> >>> yet another hardware specific variant for each of these.
> >>>
> > 
> >>> In libavcodec the hwaccels are seamlessly integrated into supported
> >>> decoders. This has been favored over separate full stream hardware
> >>> decoders where possible for the above reasons. It would be ideal to
> >>> achieve the same with libavfilter.
> > 
> >> i am in favor of this design as well. The user should not need to have
> >> to know about and manage manually GPU optimizations.
> > 
> >> thx
> > 
> > Hi! I am GSoC student and I still have some tasks before the program ends. 
> > Also my mentor said:
> >  <jkqxz> IMO don't think about it now, there isn't that much time left.
> >  <jkqxz> I looked at doing last year (when converting to the current structure) and concluded that it's not really sane to do.
> >  <jkqxz> The _opencl versions of filters operate completely differently, so while some code for setup can be shared putting them in the same filter doesn't really make sense.
> > 
> > Thanks, Danil.
> 
> Fair enough, i wasn't aware this was for GSoC, and this kind of change
> (assuming it's doable) would definitely take a while, so it can wait.

+1


[...]
Mark Thompson Aug. 4, 2018, 10:50 p.m. UTC | #6
On 25/07/18 13:13, Danil Iashchenko wrote:
> Add lumakey_opencl filter. Behaves like existing lumakey filter.
> 
> ---
>  configure                       |   1 +
>  libavfilter/Makefile            |   2 +
>  libavfilter/allfilters.c        |   1 +
>  libavfilter/opencl/lumakey.cl   |  43 +++++++
>  libavfilter/opencl_source.h     |   1 +
>  libavfilter/vf_lumakey_opencl.c | 243 ++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 291 insertions(+)
>  create mode 100644 libavfilter/opencl/lumakey.cl
>  create mode 100644 libavfilter/vf_lumakey_opencl.c


I think you need a bit more configuration to make this work without needing to force the formats externally.  I guess you did something like 'format=yuv420p,hwupload,lumakey_opencl,hwdownload,format=yuva420p'?

It should probably be able to work with something like:

./ffmpeg_g -y -init_hw_device vaapi=va:/dev/dri/renderD128 -init_hw_device opencl=cl@va -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device va -i in1.mp4 -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device va -i in2.mp4 -an -filter_hw_device cl -filter_complex '[0:v]scale_vaapi=format=yuv420p,hwmap,lumakey_opencl[a]; [1:v]scale_vaapi=format=yuv420p,hwmap[b]; [a][b]overlay_opencl,hwmap=derive_device=vaapi:reverse=1,scale_vaapi=format=nv12' -c:v h264_vaapi out.mp4

to composite two videos together keyed by the luma of the first (I'm not sure that command-line is exactly right, but something like that).  In this case there isn't any way to make the output format implicitly do the right thing, so the filter needs to deal with the formats internally.  (And extra points if you can avoid the yuv420p conversion :)


Everything else in the patch looks fine.

Thanks,

- Mark
diff mbox

Patch

diff --git a/configure b/configure
index 5783407..9816ebb 100755
--- a/configure
+++ b/configure
@@ -3356,6 +3356,7 @@  interlace_filter_deps="gpl"
 kerndeint_filter_deps="gpl"
 ladspa_filter_deps="ladspa libdl"
 lensfun_filter_deps="liblensfun version3"
+lumakey_opencl_filter_deps="opencl"
 lv2_filter_deps="lv2"
 mcdeint_filter_deps="avcodec gpl"
 movie_filter_deps="avcodec avformat"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 5d4549e..2a01bf3 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -253,6 +253,8 @@  OBJS-$(CONFIG_LIBVMAF_FILTER)                += vf_libvmaf.o framesync.o
 OBJS-$(CONFIG_LIMITER_FILTER)                += vf_limiter.o
 OBJS-$(CONFIG_LOOP_FILTER)                   += f_loop.o
 OBJS-$(CONFIG_LUMAKEY_FILTER)                += vf_lumakey.o
+OBJS-$(CONFIG_LUMAKEY_OPENCL_FILTER)         += vf_lumakey_opencl.o opencl.o \
+                                                opencl/lumakey.o
 OBJS-$(CONFIG_LUT_FILTER)                    += vf_lut.o
 OBJS-$(CONFIG_LUT2_FILTER)                   += vf_lut2.o framesync.o
 OBJS-$(CONFIG_LUT3D_FILTER)                  += vf_lut3d.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 521bc53..065ad9f 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -242,6 +242,7 @@  extern AVFilter ff_vf_libvmaf;
 extern AVFilter ff_vf_limiter;
 extern AVFilter ff_vf_loop;
 extern AVFilter ff_vf_lumakey;
+extern AVFilter ff_vf_lumakey_opencl;
 extern AVFilter ff_vf_lut;
 extern AVFilter ff_vf_lut2;
 extern AVFilter ff_vf_lut3d;
diff --git a/libavfilter/opencl/lumakey.cl b/libavfilter/opencl/lumakey.cl
new file mode 100644
index 0000000..dbee63e
--- /dev/null
+++ b/libavfilter/opencl/lumakey.cl
@@ -0,0 +1,43 @@ 
+/*
+ * Copyright (c) 2018 Danil Iashchenko
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+__kernel void lumakey_global(__write_only image2d_t dstAlpha,
+                             __read_only  image2d_t srcLuma,
+                             float w,
+                             float b,
+                             int so)
+{
+    const sampler_t sampler = (CLK_NORMALIZED_COORDS_FALSE |
+                               CLK_ADDRESS_CLAMP_TO_EDGE   |
+                               CLK_FILTER_NEAREST);
+
+    int2 loc = (int2)(get_global_id(0), get_global_id(1));
+    float4 lumaPix = read_imagef(srcLuma,  sampler, loc) * 255;
+
+    if (lumaPix.x >= b && lumaPix.x <= w) {
+        write_imagef(dstAlpha, loc, (float4)(0.0f));
+    } else if (lumaPix.x > b - so && lumaPix.x < w + so) {
+        if (lumaPix.x < b) {
+            write_imagef(dstAlpha, loc, (float4)((1 - (lumaPix.x - b + so) / so)));
+        } else {
+            write_imagef(dstAlpha, loc, (float4)(((lumaPix.x - w) / so)));
+        }
+    }
+}
diff --git a/libavfilter/opencl_source.h b/libavfilter/opencl_source.h
index de4e66e..dba701e 100644
--- a/libavfilter/opencl_source.h
+++ b/libavfilter/opencl_source.h
@@ -22,6 +22,7 @@ 
 extern const char *ff_opencl_source_avgblur;
 extern const char *ff_opencl_source_colorspace_common;
 extern const char *ff_opencl_source_convolution;
+extern const char *ff_opencl_source_lumakey;
 extern const char *ff_opencl_source_overlay;
 extern const char *ff_opencl_source_tonemap;
 extern const char *ff_opencl_source_unsharp;
diff --git a/libavfilter/vf_lumakey_opencl.c b/libavfilter/vf_lumakey_opencl.c
new file mode 100644
index 0000000..8879a88
--- /dev/null
+++ b/libavfilter/vf_lumakey_opencl.c
@@ -0,0 +1,243 @@ 
+/*
+ * Copyright (c) 2018 Danil Iashchenko
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/common.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+
+
+#include "avfilter.h"
+#include "internal.h"
+#include "opencl.h"
+#include "opencl_source.h"
+#include "video.h"
+
+typedef struct LumakeyOpenCLContext {
+    OpenCLFilterContext ocf;
+
+    int              initialised;
+    cl_kernel        kernel;
+    cl_command_queue command_queue;
+
+    cl_int threshold;
+    cl_int tolerance;
+    cl_int softness;
+
+    cl_float black;
+    cl_float white;
+
+} LumakeyOpenCLContext;
+
+static int lumakey_opencl_init(AVFilterContext *avctx)
+{
+    LumakeyOpenCLContext *ctx = avctx->priv;
+    cl_int cle;
+    int err;
+
+    err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_lumakey, 1);
+    if (err < 0)
+        goto fail;
+
+    ctx->command_queue = clCreateCommandQueue(ctx->ocf.hwctx->context,
+                                              ctx->ocf.hwctx->device_id,
+                                              0, &cle);
+    CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create OpenCL "
+                     "command queue %d.\n", cle);
+
+    ctx->kernel = clCreateKernel(ctx->ocf.program, "lumakey_global", &cle);
+    CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create "
+                     "kernel %d.\n", cle);
+
+    ctx->initialised = 1;
+    return 0;
+
+fail:
+    if (ctx->command_queue)
+        clReleaseCommandQueue(ctx->command_queue);
+    if (ctx->kernel)
+        clReleaseKernel(ctx->kernel);
+    return err;
+}
+
+
+static int lumakey_opencl_filter_frame(AVFilterLink *inlink, AVFrame *input)
+{
+    AVFilterContext *avctx = inlink->dst;
+    AVFilterLink *outlink = avctx->outputs[0];
+    LumakeyOpenCLContext *ctx = avctx->priv;
+    AVFrame *output = NULL;
+    cl_int cle;
+    size_t global_work[2];
+    cl_mem src, dst;
+    int err, i;
+    size_t origin[3] = {0, 0, 0};
+    size_t region[3] = {0, 0, 1};
+
+    av_log(ctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
+           av_get_pix_fmt_name(input->format),
+           input->width, input->height, input->pts);
+
+    if (!input->hw_frames_ctx)
+        return AVERROR(EINVAL);
+
+    if (!ctx->initialised) {
+        err = lumakey_opencl_init(avctx);
+        if (err < 0)
+            goto fail;
+
+        ctx->white = ctx->threshold + ctx->tolerance;
+        ctx->black = ctx->threshold - ctx->tolerance;
+    }
+
+    output = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!output) {
+        err = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    for (i = 0; i < FF_ARRAY_ELEMS(output->data) - 1; i++) {
+        src  = (cl_mem) input->data[i];
+        dst  = (cl_mem)output->data[i];
+
+        if (!dst)
+            break;
+
+        err = ff_opencl_filter_work_size_from_image(avctx, region, output, i, 0);
+        if (err < 0)
+            goto fail;
+
+        cle = clEnqueueCopyImage(ctx->command_queue, src, dst,
+                                 origin, origin, region, 0, NULL, NULL);
+        CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to copy plane %d: %d.\n",
+                         i, cle);
+    }
+
+    src = (cl_mem) input->data[0];
+    dst = (cl_mem)output->data[3];
+
+    CL_SET_KERNEL_ARG(ctx->kernel, 0, cl_mem,   &dst);
+    CL_SET_KERNEL_ARG(ctx->kernel, 1, cl_mem,   &src);
+    CL_SET_KERNEL_ARG(ctx->kernel, 2, cl_float, &ctx->white);
+    CL_SET_KERNEL_ARG(ctx->kernel, 3, cl_float, &ctx->black);
+    CL_SET_KERNEL_ARG(ctx->kernel, 4, cl_int,   &ctx->softness);
+
+    err = ff_opencl_filter_work_size_from_image(avctx, global_work, output, 0, 0);
+    if (err < 0)
+        goto fail;
+
+    av_log(avctx, AV_LOG_DEBUG, "Run kernel on plane %d"
+           "(%"SIZE_SPECIFIER"x%"SIZE_SPECIFIER").\n",
+           0, global_work[0], global_work[1]);
+
+    cle = clEnqueueNDRangeKernel(ctx->command_queue, ctx->kernel, 2, NULL,
+                                 global_work, NULL,
+                                 0, NULL, NULL);
+    CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to enqueue "
+                     "kernel: %d.\n", cle);
+
+
+    cle = clFinish(ctx->command_queue);
+    CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to finish command queue: %d.\n", cle);
+
+    err = av_frame_copy_props(output, input);
+    if (err < 0)
+        goto fail;
+
+    av_frame_free(&input);
+
+    av_log(ctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
+           av_get_pix_fmt_name(output->format),
+           output->width, output->height, output->pts);
+
+    return ff_filter_frame(outlink, output);
+
+fail:
+    clFinish(ctx->command_queue);
+    av_frame_free(&input);
+    av_frame_free(&output);
+    return err;
+}
+
+
+static av_cold void lumakey_opencl_uninit(AVFilterContext *avctx)
+{
+    LumakeyOpenCLContext *ctx = avctx->priv;
+    cl_int cle;
+
+    if (ctx->kernel) {
+        cle = clReleaseKernel(ctx->kernel);
+        if (cle != CL_SUCCESS)
+            av_log(avctx, AV_LOG_ERROR, "Failed to release "
+                   "kernel: %d.\n", cle);
+    }
+
+    if (ctx->command_queue) {
+        cle = clReleaseCommandQueue(ctx->command_queue);
+        if (cle != CL_SUCCESS)
+            av_log(avctx, AV_LOG_ERROR, "Failed to release "
+                   "command queue: %d.\n", cle);
+    }
+
+    ff_opencl_filter_uninit(avctx);
+}
+
+static const AVFilterPad lumakey_opencl_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = &lumakey_opencl_filter_frame,
+        .config_props = &ff_opencl_filter_config_input,
+    },
+    { NULL }
+};
+
+static const AVFilterPad lumakey_opencl_outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = &ff_opencl_filter_config_output,
+    },
+    { NULL }
+};
+
+#define OFFSET(x) offsetof(LumakeyOpenCLContext, x)
+#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
+
+static const AVOption lumakey_opencl_options[] = {
+    { "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
+    { "tolerance", "set the tolerance value", OFFSET(tolerance), AV_OPT_TYPE_INT, {.i64=1}, 0, UINT16_MAX, FLAGS },
+    { "softness",  "set the softness value",  OFFSET(softness),  AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(lumakey_opencl);
+
+AVFilter ff_vf_lumakey_opencl = {
+    .name           = "lumakey_opencl",
+    .description    = NULL_IF_CONFIG_SMALL("Apply convolution mask to input video"),
+    .priv_size      = sizeof(LumakeyOpenCLContext),
+    .priv_class     = &lumakey_opencl_class,
+    .init           = &ff_opencl_filter_init,
+    .uninit         = &lumakey_opencl_uninit,
+    .query_formats  = &ff_opencl_filter_query_formats,
+    .inputs         = lumakey_opencl_inputs,
+    .outputs        = lumakey_opencl_outputs,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+};