diff --git a/libavfilter/vf_colorlevels.c b/libavfilter/vf_colorlevels.c
index 5385a5e754..f8645a08bd 100644
--- a/libavfilter/vf_colorlevels.c
+++ b/libavfilter/vf_colorlevels.c
@@ -26,6 +26,7 @@
 #include "formats.h"
 #include "internal.h"
 #include "video.h"
+#include "thread.h"
 
 #define R 0
 #define G 1
@@ -37,6 +38,11 @@ typedef struct Range {
     double out_min, out_max;
 } Range;
 
+typedef struct ThreadData {
+    AVFrame *in;
+    AVFrame *out;
+} ThreadData;
+
 typedef struct ColorLevelsContext {
     const AVClass *class;
     Range range[4];
@@ -45,6 +51,7 @@ typedef struct ColorLevelsContext {
     int step;
     uint8_t rgba_map[4];
     int linesize;
+    int (*colorlevels_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
 } ColorLevelsContext;
 
 #define OFFSET(x) offsetof(ColorLevelsContext, x)
@@ -90,6 +97,68 @@ static int query_formats(AVFilterContext *ctx)
     return ff_set_common_formats(ctx, fmts_list);
 }
 
+#define DEFINE_COLORLEVELS(type, nbits)                                                      \
+static int do_##nbits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)    \
+{                                                                                            \
+    ColorLevelsContext *s = ctx->priv;                                                       \
+    AVFilterLink *inlink = ctx->inputs[0];                                                   \
+    const int step = s->step;                                                                \
+    int x, y, i;                                                                             \
+    ThreadData *td = arg;                                                                    \
+    const AVFrame *in = td->in;                                                              \
+    AVFrame *out = td->out;                                                                  \
+                                                                                             \
+    for (i = 0; i < s->nb_comp; i++) {                                                       \
+        Range *r = &s->range[i];                                                             \
+        const int slice_start = (inlink->h * jobnr) / nb_jobs;                               \
+        const int slice_end = (inlink->h * (jobnr+1)) / nb_jobs;                             \
+        const uint8_t offset = s->rgba_map[i];                                               \
+        const uint8_t *srcrow = in->data[0] + slice_start * in->linesize[0];                 \
+        uint8_t *dstrow = out->data[0] + slice_start * out->linesize[0];                     \
+        int imin = lrint(r->in_min  * UINT##nbits##_MAX);                                    \
+        int imax = lrint(r->in_max  * UINT##nbits##_MAX);                                    \
+        int omin = lrint(r->out_min * UINT##nbits##_MAX);                                    \
+        int omax = lrint(r->out_max * UINT##nbits##_MAX);                                    \
+        double coeff;                                                                        \
+                                                                                             \
+        if (imin < 0) {                                                                      \
+            imin = UINT##nbits##_MAX;                                                        \
+            for (y = slice_start; y < slice_end; y++) {                                      \
+                const type *src = (const type *)srcrow;                                      \
+                                                                                             \
+                for (x = 0; x < s->linesize; x += step)                                      \
+                    imin = FFMIN(imin, src[x + offset]);                                     \
+                srcrow += in->linesize[0];                                                   \
+            }                                                                                \
+        }                                                                                    \
+        if (imax < 0) {                                                                      \
+            imax = 0;                                                                        \
+            for (y = slice_start; y < slice_end; y++) {                                      \
+                const type *src = (const type *)srcrow;                                      \
+                                                                                             \
+                for (x = 0; x < s->linesize; x += step)                                      \
+                    imax = FFMAX(imax, src[x + offset]);                                     \
+                srcrow += in->linesize[0];                                                   \
+            }                                                                                \
+        }                                                                                    \
+                                                                                             \
+        coeff = (omax - omin) / (double)(imax - imin);                                       \
+        for (y = slice_start; y < slice_end; y++) {                                          \
+            const type *src = (const type*)srcrow;                                           \
+            type *dst = (type *)dstrow;                                                      \
+                                                                                             \
+            for (x = 0; x < s->linesize; x += step)                                          \
+                dst[x + offset] = av_clip_uint##nbits(                                       \
+                        (src[x + offset] - imin) * coeff + omin);                            \
+            dstrow += out->linesize[0];                                                      \
+            srcrow += in->linesize[0];                                                       \
+        }                                                                                    \
+    }                                                                                        \
+    return 0;                                                                                \
+}
+DEFINE_COLORLEVELS(uint8_t, 8)
+DEFINE_COLORLEVELS(uint16_t, 16)
+
 static int config_input(AVFilterLink *inlink)
 {
     AVFilterContext *ctx = inlink->dst;
@@ -102,17 +171,17 @@ static int config_input(AVFilterLink *inlink)
     s->linesize = inlink->w * s->step;
     ff_fill_rgba_map(s->rgba_map, inlink->format);
 
+    s->colorlevels_slice = s->bpp <= 1 ? do_8bit_slice : do_16bit_slice;
     return 0;
 }
 
 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 {
     AVFilterContext *ctx = inlink->dst;
-    ColorLevelsContext *s = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[0];
-    const int step = s->step;
+    ColorLevelsContext *s = ctx->priv;
     AVFrame *out;
-    int x, y, i;
+    ThreadData td;
 
     if (av_frame_is_writable(in)) {
         out = in;
@@ -125,101 +194,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         av_frame_copy_props(out, in);
     }
 
-    switch (s->bpp) {
-    case 1:
-        for (i = 0; i < s->nb_comp; i++) {
-            Range *r = &s->range[i];
-            const uint8_t offset = s->rgba_map[i];
-            const uint8_t *srcrow = in->data[0];
-            uint8_t *dstrow = out->data[0];
-            int imin = lrint(r->in_min  * UINT8_MAX);
-            int imax = lrint(r->in_max  * UINT8_MAX);
-            int omin = lrint(r->out_min * UINT8_MAX);
-            int omax = lrint(r->out_max * UINT8_MAX);
-            double coeff;
-
-            if (imin < 0) {
-                imin = UINT8_MAX;
-                for (y = 0; y < inlink->h; y++) {
-                    const uint8_t *src = srcrow;
-
-                    for (x = 0; x < s->linesize; x += step)
-                        imin = FFMIN(imin, src[x + offset]);
-                    srcrow += in->linesize[0];
-                }
-            }
-            if (imax < 0) {
-                srcrow = in->data[0];
-                imax = 0;
-                for (y = 0; y < inlink->h; y++) {
-                    const uint8_t *src = srcrow;
-
-                    for (x = 0; x < s->linesize; x += step)
-                        imax = FFMAX(imax, src[x + offset]);
-                    srcrow += in->linesize[0];
-                }
-            }
-
-            srcrow = in->data[0];
-            coeff = (omax - omin) / (double)(imax - imin);
-            for (y = 0; y < inlink->h; y++) {
-                const uint8_t *src = srcrow;
-                uint8_t *dst = dstrow;
-
-                for (x = 0; x < s->linesize; x += step)
-                    dst[x + offset] = av_clip_uint8((src[x + offset] - imin) * coeff + omin);
-                dstrow += out->linesize[0];
-                srcrow += in->linesize[0];
-            }
-        }
-        break;
-    case 2:
-        for (i = 0; i < s->nb_comp; i++) {
-            Range *r = &s->range[i];
-            const uint8_t offset = s->rgba_map[i];
-            const uint8_t *srcrow = in->data[0];
-            uint8_t *dstrow = out->data[0];
-            int imin = lrint(r->in_min  * UINT16_MAX);
-            int imax = lrint(r->in_max  * UINT16_MAX);
-            int omin = lrint(r->out_min * UINT16_MAX);
-            int omax = lrint(r->out_max * UINT16_MAX);
-            double coeff;
-
-            if (imin < 0) {
-                imin = UINT16_MAX;
-                for (y = 0; y < inlink->h; y++) {
-                    const uint16_t *src = (const uint16_t *)srcrow;
-
-                    for (x = 0; x < s->linesize; x += step)
-                        imin = FFMIN(imin, src[x + offset]);
-                    srcrow += in->linesize[0];
-                }
-            }
-            if (imax < 0) {
-                srcrow = in->data[0];
-                imax = 0;
-                for (y = 0; y < inlink->h; y++) {
-                    const uint16_t *src = (const uint16_t *)srcrow;
-
-                    for (x = 0; x < s->linesize; x += step)
-                        imax = FFMAX(imax, src[x + offset]);
-                    srcrow += in->linesize[0];
-                }
-            }
-
-            srcrow = in->data[0];
-            coeff = (omax - omin) / (double)(imax - imin);
-            for (y = 0; y < inlink->h; y++) {
-                const uint16_t *src = (const uint16_t*)srcrow;
-                uint16_t *dst = (uint16_t *)dstrow;
-
-                for (x = 0; x < s->linesize; x += step)
-                    dst[x + offset] = av_clip_uint16((src[x + offset] - imin) * coeff + omin);
-                dstrow += out->linesize[0];
-                srcrow += in->linesize[0];
-            }
-        }
-    }
+    td.in = in;
+    td.out = out;
+    ctx->internal->execute(ctx, s->colorlevels_slice, &td, NULL,
+            FFMIN(inlink->h, ff_filter_get_nb_threads(ctx)));
 
     if (in != out)
         av_frame_free(&in);
@@ -252,5 +230,5 @@ AVFilter ff_vf_colorlevels = {
     .query_formats = query_formats,
     .inputs        = colorlevels_inputs,
     .outputs       = colorlevels_outputs,
-    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
 };
