]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/vf_lut3d.c
avfilter: Constify all AVFilters
[ffmpeg] / libavfilter / vf_lut3d.c
index 6730a424cede59a076ba42c06341c81a28d8df01..c86585cf09cfddb84cfeef0cf55961788cb2f8c0 100644 (file)
@@ -49,6 +49,8 @@ enum interp_mode {
     INTERPOLATE_NEAREST,
     INTERPOLATE_TRILINEAR,
     INTERPOLATE_TETRAHEDRAL,
+    INTERPOLATE_PYRAMID,
+    INTERPOLATE_PRISM,
     NB_INTERP_MODE
 };
 
@@ -98,16 +100,19 @@ typedef struct ThreadData {
 
 #define OFFSET(x) offsetof(LUT3DContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
 #define COMMON_OPTIONS \
-    { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, FLAGS, "interp_mode" }, \
-        { "nearest",     "use values from the nearest defined points",            0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST},     INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
-        { "trilinear",   "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR},   INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
-        { "tetrahedral", "interpolate values using a tetrahedron",                0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
+    { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, TFLAGS, "interp_mode" }, \
+        { "nearest",     "use values from the nearest defined points",            0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST},     0, 0, TFLAGS, "interp_mode" }, \
+        { "trilinear",   "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR},   0, 0, TFLAGS, "interp_mode" }, \
+        { "tetrahedral", "interpolate values using a tetrahedron",                0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, 0, TFLAGS, "interp_mode" }, \
+        { "pyramid",     "interpolate values using a pyramid",                    0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PYRAMID},     0, 0, TFLAGS, "interp_mode" }, \
+        { "prism",       "interpolate values using a prism",                      0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PRISM},       0, 0, TFLAGS, "interp_mode" }, \
     { NULL }
 
 #define EXPONENT_MASK 0x7F800000
 #define MANTISSA_MASK 0x007FFFFF
-#define SIGN_MASK     0x7FFFFFFF
+#define SIGN_MASK     0x80000000
 
 static inline float sanitizef(float f)
 {
@@ -120,7 +125,7 @@ static inline float sanitizef(float f)
             return 0.0f;
         } else if (t.i & SIGN_MASK) {
             // -INF
-            return FLT_MIN;
+            return -FLT_MAX;
         } else {
             // +INF
             return FLT_MAX;
@@ -185,6 +190,101 @@ static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d,
     return c;
 }
 
+static inline struct rgbvec interp_pyramid(const LUT3DContext *lut3d,
+                                           const struct rgbvec *s)
+{
+    const int lutsize2 = lut3d->lutsize2;
+    const int lutsize  = lut3d->lutsize;
+    const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
+    const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
+    const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]};
+    const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]];
+    const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]];
+    struct rgbvec c;
+
+    if (d.g > d.r && d.b > d.r) {
+        const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]];
+        const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]];
+        const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]];
+
+        c.r = c000.r + (c111.r - c011.r) * d.r + (c010.r - c000.r) * d.g + (c001.r - c000.r) * d.b +
+              (c011.r - c001.r - c010.r + c000.r) * d.g * d.b;
+        c.g = c000.g + (c111.g - c011.g) * d.r + (c010.g - c000.g) * d.g + (c001.g - c000.g) * d.b +
+              (c011.g - c001.g - c010.g + c000.g) * d.g * d.b;
+        c.b = c000.b + (c111.b - c011.b) * d.r + (c010.b - c000.b) * d.g + (c001.b - c000.b) * d.b +
+              (c011.b - c001.b - c010.b + c000.b) * d.g * d.b;
+    } else if (d.r > d.g && d.b > d.g) {
+        const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]];
+        const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]];
+        const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]];
+
+        c.r = c000.r + (c100.r - c000.r) * d.r + (c111.r - c101.r) * d.g + (c001.r - c000.r) * d.b +
+              (c101.r - c001.r - c100.r + c000.r) * d.r * d.b;
+        c.g = c000.g + (c100.g - c000.g) * d.r + (c111.g - c101.g) * d.g + (c001.g - c000.g) * d.b +
+              (c101.g - c001.g - c100.g + c000.g) * d.r * d.b;
+        c.b = c000.b + (c100.b - c000.b) * d.r + (c111.b - c101.b) * d.g + (c001.b - c000.b) * d.b +
+              (c101.b - c001.b - c100.b + c000.b) * d.r * d.b;
+    } else {
+        const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]];
+        const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]];
+        const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]];
+
+        c.r = c000.r + (c100.r - c000.r) * d.r + (c010.r - c000.r) * d.g + (c111.r - c110.r) * d.b +
+              (c110.r - c100.r - c010.r + c000.r) * d.r * d.g;
+        c.g = c000.g + (c100.g - c000.g) * d.r + (c010.g - c000.g) * d.g + (c111.g - c110.g) * d.b +
+              (c110.g - c100.g - c010.g + c000.g) * d.r * d.g;
+        c.b = c000.b + (c100.b - c000.b) * d.r + (c010.b - c000.b) * d.g + (c111.b - c110.b) * d.b +
+              (c110.b - c100.b - c010.b + c000.b) * d.r * d.g;
+    }
+
+    return c;
+}
+
+static inline struct rgbvec interp_prism(const LUT3DContext *lut3d,
+                                         const struct rgbvec *s)
+{
+    const int lutsize2 = lut3d->lutsize2;
+    const int lutsize  = lut3d->lutsize;
+    const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
+    const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
+    const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]};
+    const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]];
+    const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]];
+    const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]];
+    const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]];
+    struct rgbvec c;
+
+    if (d.b > d.r) {
+        const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]];
+        const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]];
+
+        c.r = c000.r + (c001.r - c000.r) * d.b + (c101.r - c001.r) * d.r + (c010.r - c000.r) * d.g +
+              (c000.r - c010.r - c001.r + c011.r) * d.b * d.g +
+              (c001.r - c011.r - c101.r + c111.r) * d.r * d.g;
+        c.g = c000.g + (c001.g - c000.g) * d.b + (c101.g - c001.g) * d.r + (c010.g - c000.g) * d.g +
+              (c000.g - c010.g - c001.g + c011.g) * d.b * d.g +
+              (c001.g - c011.g - c101.g + c111.g) * d.r * d.g;
+        c.b = c000.b + (c001.b - c000.b) * d.b + (c101.b - c001.b) * d.r + (c010.b - c000.b) * d.g +
+              (c000.b - c010.b - c001.b + c011.b) * d.b * d.g +
+              (c001.b - c011.b - c101.b + c111.b) * d.r * d.g;
+    } else {
+        const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]];
+        const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]];
+
+        c.r = c000.r + (c101.r - c100.r) * d.b + (c100.r - c000.r) * d.r + (c010.r - c000.r) * d.g +
+              (c100.r - c110.r - c101.r + c111.r) * d.b * d.g +
+              (c000.r - c010.r - c100.r + c110.r) * d.r * d.g;
+        c.g = c000.g + (c101.g - c100.g) * d.b + (c100.g - c000.g) * d.r + (c010.g - c000.g) * d.g +
+              (c100.g - c110.g - c101.g + c111.g) * d.b * d.g +
+              (c000.g - c010.g - c100.g + c110.g) * d.r * d.g;
+        c.b = c000.b + (c101.b - c100.b) * d.b + (c100.b - c000.b) * d.r + (c010.b - c000.b) * d.g +
+              (c100.b - c110.b - c101.b + c111.b) * d.b * d.g +
+              (c000.b - c010.b - c100.b + c110.b) * d.r * d.g;
+    }
+
+    return c;
+}
+
 /**
  * Tetrahedral interpolation. Based on code found in Truelight Software Library paper.
  * @see http://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf
@@ -337,26 +437,38 @@ static int interp_##nbits##_##name##_p##depth(AVFilterContext *ctx, void *arg, i
 DEFINE_INTERP_FUNC_PLANAR(nearest,     8, 8)
 DEFINE_INTERP_FUNC_PLANAR(trilinear,   8, 8)
 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 8, 8)
+DEFINE_INTERP_FUNC_PLANAR(pyramid,     8, 8)
+DEFINE_INTERP_FUNC_PLANAR(prism,       8, 8)
 
 DEFINE_INTERP_FUNC_PLANAR(nearest,     16, 9)
 DEFINE_INTERP_FUNC_PLANAR(trilinear,   16, 9)
 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 9)
+DEFINE_INTERP_FUNC_PLANAR(pyramid,     16, 9)
+DEFINE_INTERP_FUNC_PLANAR(prism,       16, 9)
 
 DEFINE_INTERP_FUNC_PLANAR(nearest,     16, 10)
 DEFINE_INTERP_FUNC_PLANAR(trilinear,   16, 10)
 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 10)
+DEFINE_INTERP_FUNC_PLANAR(pyramid,     16, 10)
+DEFINE_INTERP_FUNC_PLANAR(prism,       16, 10)
 
 DEFINE_INTERP_FUNC_PLANAR(nearest,     16, 12)
 DEFINE_INTERP_FUNC_PLANAR(trilinear,   16, 12)
 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 12)
+DEFINE_INTERP_FUNC_PLANAR(pyramid,     16, 12)
+DEFINE_INTERP_FUNC_PLANAR(prism,       16, 12)
 
 DEFINE_INTERP_FUNC_PLANAR(nearest,     16, 14)
 DEFINE_INTERP_FUNC_PLANAR(trilinear,   16, 14)
 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 14)
+DEFINE_INTERP_FUNC_PLANAR(pyramid,     16, 14)
+DEFINE_INTERP_FUNC_PLANAR(prism,       16, 14)
 
 DEFINE_INTERP_FUNC_PLANAR(nearest,     16, 16)
 DEFINE_INTERP_FUNC_PLANAR(trilinear,   16, 16)
 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 16)
+DEFINE_INTERP_FUNC_PLANAR(pyramid,     16, 16)
+DEFINE_INTERP_FUNC_PLANAR(prism,       16, 16)
 
 #define DEFINE_INTERP_FUNC_PLANAR_FLOAT(name, depth)                                                   \
 static int interp_##name##_pf##depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)          \
@@ -422,6 +534,8 @@ static int interp_##name##_pf##depth(AVFilterContext *ctx, void *arg, int jobnr,
 DEFINE_INTERP_FUNC_PLANAR_FLOAT(nearest,     32)
 DEFINE_INTERP_FUNC_PLANAR_FLOAT(trilinear,   32)
 DEFINE_INTERP_FUNC_PLANAR_FLOAT(tetrahedral, 32)
+DEFINE_INTERP_FUNC_PLANAR_FLOAT(pyramid,     32)
+DEFINE_INTERP_FUNC_PLANAR_FLOAT(prism,       32)
 
 #define DEFINE_INTERP_FUNC(name, nbits)                                                             \
 static int interp_##nbits##_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)         \
@@ -475,10 +589,14 @@ static int interp_##nbits##_##name(AVFilterContext *ctx, void *arg, int jobnr, i
 DEFINE_INTERP_FUNC(nearest,     8)
 DEFINE_INTERP_FUNC(trilinear,   8)
 DEFINE_INTERP_FUNC(tetrahedral, 8)
+DEFINE_INTERP_FUNC(pyramid,     8)
+DEFINE_INTERP_FUNC(prism,       8)
 
 DEFINE_INTERP_FUNC(nearest,     16)
 DEFINE_INTERP_FUNC(trilinear,   16)
 DEFINE_INTERP_FUNC(tetrahedral, 16)
+DEFINE_INTERP_FUNC(pyramid,     16)
+DEFINE_INTERP_FUNC(prism,       16)
 
 #define MAX_LINE_SIZE 512
 
@@ -878,18 +996,16 @@ static int parse_cinespace(AVFilterContext *ctx, FILE *f)
 
                     prelut_sizes[i] = npoints;
                     in_min[i] = FLT_MAX;
-                    in_max[i] = FLT_MIN;
+                    in_max[i] = -FLT_MAX;
                     out_min[i] = FLT_MAX;
-                    out_max[i] = FLT_MIN;
-
-                    last = FLT_MIN;
+                    out_max[i] = -FLT_MAX;
 
                     for (int j = 0; j < npoints; j++) {
                         NEXT_FLOAT_OR_GOTO(v, end)
                         in_min[i] = FFMIN(in_min[i], v);
                         in_max[i] = FFMAX(in_max[i], v);
                         in_prelut[i][j] = v;
-                        if (v < last) {
+                        if (j > 0 && v < last) {
                             av_log(ctx, AV_LOG_ERROR, "Invalid file, non increasing prelut.\n");
                             ret = AVERROR(ENOMEM);
                             goto end;
@@ -1088,6 +1204,8 @@ static int config_input(AVFilterLink *inlink)
     case INTERPOLATE_NEAREST:     SET_FUNC(nearest);        break;
     case INTERPOLATE_TRILINEAR:   SET_FUNC(trilinear);      break;
     case INTERPOLATE_TETRAHEDRAL: SET_FUNC(tetrahedral);    break;
+    case INTERPOLATE_PYRAMID:     SET_FUNC(pyramid);        break;
+    case INTERPOLATE_PRISM:       SET_FUNC(prism);          break;
     default:
         av_assert0(0);
     }
@@ -1133,6 +1251,18 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     return ff_filter_frame(outlink, out);
 }
 
+static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
+                           char *res, int res_len, int flags)
+{
+    int ret;
+
+    ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
+    if (ret < 0)
+        return ret;
+
+    return config_input(ctx->inputs[0]);
+}
+
 #if CONFIG_LUT3D_FILTER
 static const AVOption lut3d_options[] = {
     { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
@@ -1223,7 +1353,7 @@ static const AVFilterPad lut3d_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_lut3d = {
+const AVFilter ff_vf_lut3d = {
     .name          = "lut3d",
     .description   = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."),
     .priv_size     = sizeof(LUT3DContext),
@@ -1234,6 +1364,7 @@ AVFilter ff_vf_lut3d = {
     .outputs       = lut3d_outputs,
     .priv_class    = &lut3d_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .process_command = process_command,
 };
 #endif
 
@@ -1489,7 +1620,7 @@ static const AVFilterPad haldclut_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_haldclut = {
+const AVFilter ff_vf_haldclut = {
     .name          = "haldclut",
     .description   = NULL_IF_CONFIG_SMALL("Adjust colors using a Hald CLUT."),
     .priv_size     = sizeof(LUT3DContext),
@@ -1502,6 +1633,7 @@ AVFilter ff_vf_haldclut = {
     .outputs       = haldclut_outputs,
     .priv_class    = &haldclut_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .process_command = process_command,
 };
 #endif
 
@@ -1681,13 +1813,13 @@ try_again:
 }
 
 static const AVOption lut1d_options[] = {
-    { "file", "set 1D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
-    { "interp", "select interpolation mode", OFFSET(interpolation),    AV_OPT_TYPE_INT, {.i64=INTERPOLATE_1D_LINEAR}, 0, NB_INTERP_1D_MODE-1, FLAGS, "interp_mode" },
-        { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_NEAREST},   INT_MIN, INT_MAX, FLAGS, "interp_mode" },
-        { "linear",  "use values from the linear interpolation",   0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_LINEAR},    INT_MIN, INT_MAX, FLAGS, "interp_mode" },
-        { "cosine",  "use values from the cosine interpolation",   0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_COSINE},    INT_MIN, INT_MAX, FLAGS, "interp_mode" },
-        { "cubic",   "use values from the cubic interpolation",    0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_CUBIC},     INT_MIN, INT_MAX, FLAGS, "interp_mode" },
-        { "spline",  "use values from the spline interpolation",   0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_SPLINE},    INT_MIN, INT_MAX, FLAGS, "interp_mode" },
+    { "file", "set 1D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = TFLAGS },
+    { "interp", "select interpolation mode", OFFSET(interpolation),    AV_OPT_TYPE_INT, {.i64=INTERPOLATE_1D_LINEAR}, 0, NB_INTERP_1D_MODE-1, TFLAGS, "interp_mode" },
+        { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_NEAREST},   0, 0, TFLAGS, "interp_mode" },
+        { "linear",  "use values from the linear interpolation",   0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_LINEAR},    0, 0, TFLAGS, "interp_mode" },
+        { "cosine",  "use values from the cosine interpolation",   0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_COSINE},    0, 0, TFLAGS, "interp_mode" },
+        { "cubic",   "use values from the cubic interpolation",    0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_CUBIC},     0, 0, TFLAGS, "interp_mode" },
+        { "spline",  "use values from the spline interpolation",   0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_SPLINE},    0, 0, TFLAGS, "interp_mode" },
     { NULL }
 };
 
@@ -2117,6 +2249,24 @@ static int filter_frame_1d(AVFilterLink *inlink, AVFrame *in)
     return ff_filter_frame(outlink, out);
 }
 
+static int lut1d_process_command(AVFilterContext *ctx, const char *cmd, const char *args,
+                           char *res, int res_len, int flags)
+{
+    LUT1DContext *lut1d = ctx->priv;
+    int ret;
+
+    ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
+    if (ret < 0)
+        return ret;
+
+    ret = lut1d_init(ctx);
+    if (ret < 0) {
+        set_identity_matrix_1d(lut1d, 32);
+        return ret;
+    }
+    return config_input_1d(ctx->inputs[0]);
+}
+
 static const AVFilterPad lut1d_inputs[] = {
     {
         .name         = "default",
@@ -2135,7 +2285,7 @@ static const AVFilterPad lut1d_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_lut1d = {
+const AVFilter ff_vf_lut1d = {
     .name          = "lut1d",
     .description   = NULL_IF_CONFIG_SMALL("Adjust colors using a 1D LUT."),
     .priv_size     = sizeof(LUT1DContext),
@@ -2145,5 +2295,6 @@ AVFilter ff_vf_lut1d = {
     .outputs       = lut1d_outputs,
     .priv_class    = &lut1d_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .process_command = lut1d_process_command,
 };
 #endif