diff --git a/doc/filters.texi b/doc/filters.texi
index bfa8ccec8b..53814e003b 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -21797,6 +21797,22 @@ Default value is @code{10.}.
 @item sc_pass, s
 Set the flag to pass scene change frames to the next filter. Default value
is @code{0}
 You can enable it if you want to get snapshot of scene change frames only.
+
+@item mode
+Set the scene change detection method. Default value is @code{-1}
+Available values are:
+
+@table @samp
+@item -1
+Legacy mode for sum of absolute linear differences. Compare frame with
previous only and no delay.
+
+@item 0
+Sum of absolute linear differences. Compare frame with both previous and
next which introduces a 1 frame delay.
+
+@item 1
+Sum of mean of cubic root differences. Compare frame with both previous and
next which introduces a 1 frame delay.
+
+@end table
 @end table
 
 @anchor{selectivecolor}
diff --git a/libavfilter/scene_sad.c b/libavfilter/scene_sad.c
index caf911eb5d..9b80d426bc 100644
--- a/libavfilter/scene_sad.c
+++ b/libavfilter/scene_sad.c
@@ -21,6 +21,7 @@
  * Scene SAD functions
  */
 
+#include "libavutil/thread.h"
 #include "scene_sad.h"
 
 void ff_scene_sad16_c(SCENE_SAD_PARAMS)
@@ -71,3 +72,153 @@ ff_scene_sad_fn ff_scene_sad_get_fn(int depth)
     return sad;
 }
 
+static AVMutex cbrt_mutex = AV_MUTEX_INITIALIZER;
+static uint8_t *cbrt_table[16] = { NULL };
+static int cbrt_table_ref[16] = { 0 };
+
+int ff_init_cbrt(int bitdepth)
+{
+    if (bitdepth < 4 || bitdepth > 16)
+        return AVERROR(EINVAL);
+
+    ff_mutex_lock(&cbrt_mutex);
+
+    uint8_t *table = cbrt_table[bitdepth];
+    if (table) {
+        cbrt_table_ref[bitdepth]++;
+        goto end;
+    }
+
+    table = av_malloc((1 << bitdepth) * (bitdepth > 8 ? 2 : 1));
+    if (!table)
+        goto end;
+    cbrt_table[bitdepth] = table;
+    cbrt_table_ref[bitdepth] = 1;
+
+    int size = 1 << bitdepth;
+    double factor = pow(size - 1, 2. / 3.);
+    if (bitdepth <= 8) {
+        for (int i = 0; i < size; i++)
+            table[i] = round(factor * pow(i, 1. / 3.));
+    } else {
+        uint16_t *tablew = (uint16_t*)table;
+        for (int i = 0; i < size; i++)
+            tablew[i] = round(factor * pow(i, 1. / 3.));
+    }
+
+end:
+    ff_mutex_unlock(&cbrt_mutex);
+    return table != NULL;
+}
+
+void ff_uninit_cbrt(int bitdepth)
+{
+    if (bitdepth < 4 || bitdepth > 16)
+        return;
+    ff_mutex_lock(&cbrt_mutex);
+    if (!--cbrt_table_ref[bitdepth]) {
+        av_free(cbrt_table[bitdepth]);
+        cbrt_table[bitdepth] = NULL;
+    }
+    ff_mutex_unlock(&cbrt_mutex);
+}
+
+void ff_scene_scrd_c(SCENE_SAD_PARAMS)
+{
+    uint64_t scrdPlus = 0;
+    uint64_t scrdMinus = 0;
+    int x, y;
+
+    uint8_t *table = cbrt_table[8];
+    if (!table) {
+        *sum = 0;
+        return;
+    }
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            if (src1[x] > src2[x])
+                scrdMinus += table[src1[x] - src2[x]];
+            else
+                scrdPlus += table[src2[x] - src1[x]];
+        src1 += stride1;
+        src2 += stride2;
+    }
+
+    double mean = (sqrt(scrdPlus) + sqrt(scrdMinus)) / 2.0;
+    *sum = 2.0 * mean * mean;
+}
+
+void ff_scene_scrd2B_c(SCENE_SAD_PARAMS, int bitdepth)
+{
+    uint64_t scrdPlus = 0;
+    uint64_t scrdMinus = 0;
+    const uint16_t *src1w = (const uint16_t*)src1;
+    const uint16_t *src2w = (const uint16_t*)src2;
+    int x, y;
+
+    uint16_t *table = (uint16_t*)cbrt_table[bitdepth];
+    if (!table) {
+        *sum = 0;
+        return;
+    }
+
+    stride1 /= 2;
+    stride2 /= 2;
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            if (src1w[x] > src2w[x])
+                scrdMinus += table[src1w[x] - src2w[x]];
+            else
+                scrdPlus += table[src2w[x] - src1w[x]];
+        src1w += stride1;
+        src2w += stride2;
+    }
+
+    double mean = (sqrt(scrdPlus) + sqrt(scrdMinus)) / 2.0;
+    *sum = 2.0 * mean * mean;
+}
+
+void ff_scene_scrd9_c(SCENE_SAD_PARAMS)
+{
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum, 9);
+}
+
+void ff_scene_scrd10_c(SCENE_SAD_PARAMS)
+{
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum,
10);
+}
+
+void ff_scene_scrd12_c(SCENE_SAD_PARAMS)
+{
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum,
12);
+}
+
+void ff_scene_scrd14_c(SCENE_SAD_PARAMS)
+{
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum,
14);
+}
+
+void ff_scene_scrd16_c(SCENE_SAD_PARAMS)
+{
+    ff_scene_scrd2B_c(src1, stride1, src2, stride2, width, height, sum,
16);
+}
+
+ff_scene_sad_fn ff_scene_scrd_get_fn(int depth)
+{
+    ff_scene_sad_fn scrd = NULL;
+    if (depth == 8)
+        scrd = ff_scene_scrd_c;
+    else if (depth == 9)
+        scrd = ff_scene_scrd9_c;
+    else if (depth == 10)
+        scrd = ff_scene_scrd10_c;
+    else if (depth == 12)
+        scrd = ff_scene_scrd12_c;
+    else if (depth == 14)
+        scrd = ff_scene_scrd14_c;
+    else if (depth == 16)
+        scrd = ff_scene_scrd16_c;
+    return scrd;
+}
diff --git a/libavfilter/scene_sad.h b/libavfilter/scene_sad.h
index 173a051f2b..c294bd90f9 100644
--- a/libavfilter/scene_sad.h
+++ b/libavfilter/scene_sad.h
@@ -41,4 +41,10 @@ ff_scene_sad_fn ff_scene_sad_get_fn_x86(int depth);
 
 ff_scene_sad_fn ff_scene_sad_get_fn(int depth);
 
+ff_scene_sad_fn ff_scene_scrd_get_fn(int depth);
+
+int ff_init_cbrt(int bitdepth);
+
+void ff_uninit_cbrt(int bitdepth);
+
 #endif /* AVFILTER_SCENE_SAD_H */
diff --git a/libavfilter/vf_scdet.c b/libavfilter/vf_scdet.c
index 15399cfebf..93da5837b3 100644
--- a/libavfilter/vf_scdet.c
+++ b/libavfilter/vf_scdet.c
@@ -31,6 +31,18 @@
 #include "scene_sad.h"
 #include "video.h"
 
+enum SCDETMode {
+    MODE_LEGACY = -1,
+    MODE_LINEAR = 0,
+    MODE_MEAN_CBRT = 1
+};
+
+typedef struct SCDETFrameInfo {
+    AVFrame *picref;
+    double mafd;
+    double diff;
+} SCDETFrameInfo;
+
 typedef struct SCDetContext {
     const AVClass *class;
 
@@ -39,11 +51,12 @@ typedef struct SCDetContext {
     int nb_planes;
     int bitdepth;
     ff_scene_sad_fn sad;
-    double prev_mafd;
-    double scene_score;
-    AVFrame *prev_picref;
+    SCDETFrameInfo curr_frame;
+    SCDETFrameInfo prev_frame;
+
     double threshold;
     int sc_pass;
+    enum SCDETMode mode;
 } SCDetContext;
 
 #define OFFSET(x) offsetof(SCDetContext, x)
@@ -55,6 +68,7 @@ static const AVOption scdet_options[] = {
     { "t",           "set scene change detect threshold",
OFFSET(threshold),  AV_OPT_TYPE_DOUBLE,   {.dbl = 10.},     0,  100., V|F },
     { "sc_pass",     "Set the flag to pass scene change frames",
OFFSET(sc_pass),    AV_OPT_TYPE_BOOL,     {.dbl =  0  },    0,    1,  V|F },
     { "s",           "Set the flag to pass scene change frames",
OFFSET(sc_pass),    AV_OPT_TYPE_BOOL,     {.dbl =  0  },    0,    1,  V|F },
+    { "mode",        "scene change detection method",
OFFSET(mode),       AV_OPT_TYPE_INT,      {.i64 = MODE_LEGACY}, MODE_LEGACY,
MODE_MEAN_CBRT, V|F },
     {NULL}
 };
 
@@ -91,7 +105,14 @@ static int config_input(AVFilterLink *inlink)
         s->height[plane] = inlink->h >> ((plane == 1 || plane == 2) ?
desc->log2_chroma_h : 0);
     }
 
-    s->sad = ff_scene_sad_get_fn(s->bitdepth == 8 ? 8 : 16);
+    if (s->mode == MODE_LINEAR || s->mode == MODE_LEGACY)
+        s->sad = ff_scene_sad_get_fn(s->bitdepth == 8 ? 8 : 16);
+    else if (s->mode == MODE_MEAN_CBRT) {
+        int ret = ff_init_cbrt(s->bitdepth);
+        if (ret < 0)
+            return ret;
+        s->sad = ff_scene_scrd_get_fn(s->bitdepth);
+    }
     if (!s->sad)
         return AVERROR(EINVAL);
 
@@ -101,46 +122,97 @@ static int config_input(AVFilterLink *inlink)
 static av_cold void uninit(AVFilterContext *ctx)
 {
     SCDetContext *s = ctx->priv;
-
-    av_frame_free(&s->prev_picref);
+    if (s->mode == MODE_LEGACY)
+        av_frame_free(&s->prev_frame.picref);
+    if (s->mode == MODE_MEAN_CBRT)
+        ff_uninit_cbrt(s->bitdepth);
 }
 
-static double get_scene_score(AVFilterContext *ctx, AVFrame *frame)
+static void compute_diff(AVFilterContext *ctx)
 {
-    double ret = 0;
     SCDetContext *s = ctx->priv;
-    AVFrame *prev_picref = s->prev_picref;
+    AVFrame *prev_picref = s->prev_frame.picref;
+    AVFrame *curr_picref = s->curr_frame.picref;
 
-    if (prev_picref && frame->height == prev_picref->height
-                    && frame->width  == prev_picref->width) {
-        uint64_t sad = 0;
-        double mafd, diff;
-        uint64_t count = 0;
+    if (prev_picref && curr_picref
+            && curr_picref->height == prev_picref->height
+            && curr_picref->width  == prev_picref->width) {
 
+        uint64_t sum = 0;
+        uint64_t count = 0;
         for (int plane = 0; plane < s->nb_planes; plane++) {
-            uint64_t plane_sad;
+            uint64_t plane_sum;
             s->sad(prev_picref->data[plane], prev_picref->linesize[plane],
-                    frame->data[plane], frame->linesize[plane],
-                    s->width[plane], s->height[plane], &plane_sad);
-            sad += plane_sad;
+                    curr_picref->data[plane], curr_picref->linesize[plane],
+                    s->width[plane], s->height[plane], &plane_sum);
+            sum += plane_sum;
             count += s->width[plane] * s->height[plane];
         }
 
-        mafd = (double)sad * 100. / count / (1ULL << s->bitdepth);
-        diff = fabs(mafd - s->prev_mafd);
-        ret  = av_clipf(FFMIN(mafd, diff), 0, 100.);
-        s->prev_mafd = mafd;
-        av_frame_free(&prev_picref);
+        s->curr_frame.mafd = (double)sum * 100. / count / (1ULL <<
s->bitdepth);
+        if (s->mode == MODE_LEGACY)
+            s->curr_frame.diff = fabs(s->curr_frame.mafd -
s->prev_frame.mafd);
+        else
+            s->curr_frame.diff = s->curr_frame.mafd - s->prev_frame.mafd;
+    } else {
+        s->curr_frame.mafd = 0;
+        s->curr_frame.diff = 0;
     }
-    s->prev_picref = av_frame_clone(frame);
-    return ret;
 }
 
-static int set_meta(SCDetContext *s, AVFrame *frame, const char *key, const
char *value)
+static int set_meta(AVFrame *frame, const char *key, const char *value)
 {
     return av_dict_set(&frame->metadata, key, value, 0);
 }
 
+static int filter_frame(AVFilterContext *ctx, AVFrame *frame)
+{
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    SCDetContext *s = ctx->priv;
+
+    s->prev_frame = s->curr_frame;
+    s->curr_frame.picref = frame;
+
+    if ((s->mode != MODE_LEGACY && s->prev_frame.picref) || (s->mode ==
MODE_LEGACY && frame != NULL)) {
+        compute_diff(ctx);
+
+        if (s->mode == MODE_LEGACY) {
+            av_frame_free(&s->prev_frame.picref);
+            s->prev_frame = s->curr_frame;
+            s->curr_frame.picref = av_frame_clone(s->curr_frame.picref);
+        } else if (s->prev_frame.diff < -s->curr_frame.diff) {
+            s->prev_frame.diff = -s->curr_frame.diff;
+            s->prev_frame.mafd = s->curr_frame.mafd;
+        }
+        double scene_score = av_clipf(s->mode == MODE_LEGACY ?
FFMIN(s->prev_frame.mafd, s->prev_frame.diff) : FFMAX(s->prev_frame.diff,
0), 0, 100.);
+
+        char buf[64];
+        snprintf(buf, sizeof(buf), "%0.3f", s->prev_frame.mafd);
+        set_meta(s->prev_frame.picref, "lavfi.scd.mafd", buf);
+        snprintf(buf, sizeof(buf), "%0.3f", scene_score);
+        set_meta(s->prev_frame.picref, "lavfi.scd.score", buf);
+
+        if (scene_score >= s->threshold) {
+            av_log(s, AV_LOG_INFO, "lavfi.scd.score: %.3f, lavfi.scd.time:
%s\n",
+                scene_score, av_ts2timestr(s->prev_frame.picref->pts,
&inlink->time_base));
+            set_meta(s->prev_frame.picref, "lavfi.scd.time",
+                av_ts2timestr(s->prev_frame.picref->pts,
&inlink->time_base));
+        }
+
+        if (s->sc_pass) {
+            if (scene_score >= s->threshold)
+                return ff_filter_frame(outlink, s->prev_frame.picref);
+            else
+                av_frame_free(&s->prev_frame.picref);
+        }
+        else
+            return ff_filter_frame(outlink, s->prev_frame.picref);
+    }
+
+    return 0;
+}
+
 static int activate(AVFilterContext *ctx)
 {
     int ret;
@@ -148,6 +220,8 @@ static int activate(AVFilterContext *ctx)
     AVFilterLink *outlink = ctx->outputs[0];
     SCDetContext *s = ctx->priv;
     AVFrame *frame;
+    int64_t pts;
+    int status;
 
     FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
 
@@ -155,31 +229,17 @@ static int activate(AVFilterContext *ctx)
     if (ret < 0)
         return ret;
 
-    if (frame) {
-        char buf[64];
-        s->scene_score = get_scene_score(ctx, frame);
-        snprintf(buf, sizeof(buf), "%0.3f", s->prev_mafd);
-        set_meta(s, frame, "lavfi.scd.mafd", buf);
-        snprintf(buf, sizeof(buf), "%0.3f", s->scene_score);
-        set_meta(s, frame, "lavfi.scd.score", buf);
+    if (ret > 0)
+        return filter_frame(ctx, frame);
 
-        if (s->scene_score >= s->threshold) {
-            av_log(s, AV_LOG_INFO, "lavfi.scd.score: %.3f, lavfi.scd.time:
%s\n",
-                    s->scene_score, av_ts2timestr(frame->pts,
&inlink->time_base));
-            set_meta(s, frame, "lavfi.scd.time",
-                    av_ts2timestr(frame->pts, &inlink->time_base));
-        }
-        if (s->sc_pass) {
-            if (s->scene_score >= s->threshold)
-                return ff_filter_frame(outlink, frame);
-            else {
-                av_frame_free(&frame);
-            }
-        } else
-            return ff_filter_frame(outlink, frame);
+    if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
+        if (status == AVERROR_EOF)
+            ret = filter_frame(ctx, NULL);
+
+        ff_outlink_set_status(outlink, status, pts);
+        return ret;
     }
 
-    FF_FILTER_FORWARD_STATUS(inlink, outlink);
     FF_FILTER_FORWARD_WANTED(outlink, inlink);
 
     return FFERROR_NOT_READY;
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index ee9f0f5e40..cff48e33d9 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -672,6 +672,9 @@ SCDET_DEPS = LAVFI_INDEV FILE_PROTOCOL MOVIE_FILTER
SCDET_FILTER SCALE_FILTER \
 FATE_METADATA_FILTER-$(call ALLYES, $(SCDET_DEPS)) +=
fate-filter-metadata-scdet
 fate-filter-metadata-scdet: SRC =
$(TARGET_SAMPLES)/svq3/Vertical400kbit.sorenson3.mov
 fate-filter-metadata-scdet: CMD = run $(FILTER_METADATA_COMMAND)
"sws_flags=+accurate_rnd+bitexact;movie='$(SRC)',scdet=s=1"
+FATE_METADATA_FILTER-$(call ALLYES, $(SCDET_DEPS)) +=
fate-filter-metadata-scdet1
