This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

The following commit(s) were added to refs/heads/master by this push:
     new c182b1c09c Add new mode to mpdecimate video filter
c182b1c09c is described below

commit c182b1c09c086deb62545393cc7b4a829d541508
Author:     StachowiakDawid <[email protected]>
AuthorDate: Thu Jan 2 14:54:03 2025 +0100
Commit:     StachowiakDawid <[email protected]>
CommitDate: Mon Jul 20 17:49:47 2026 +0000

    Add new mode to mpdecimate video filter
---
 doc/filters.texi                              |  16 +++-
 libavfilter/vf_mpdecimate.c                   | 111 +++++++++++++++++++++-----
 tests/fate/filter-video.mak                   |   6 ++
 tests/ref/fate/filter-mpdecimate-mode-1       |  10 +++
 tests/ref/fate/filter-mpdecimate-mode-1-min-3 |  12 +++
 5 files changed, 133 insertions(+), 22 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 002979ba08..985a1615a9 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -18721,16 +18721,28 @@ fixing movies that were inverse-telecined incorrectly.
 A description of the accepted options follows.
 
 @table @option
+@item mode
+Set the mode. If the value is 0, the filter drops similar frames.
+If the value is 1, the filter keeps only frames that are one representative of 
sufficiently long duplicate runs.
+
+Default value is 0.
+
 @item max
-Set the maximum number of consecutive frames which can be dropped (if
+For mode 0: Set the maximum number of consecutive frames which can be dropped 
(if
 positive), or the minimum interval between dropped frames (if
 negative). If the value is 0, the frame is dropped disregarding the
 number of previous sequentially dropped frames.
 
 Default value is 0.
 
+@item min
+For mode 1: Set the minimum number of frame duplicates to mark the frame as 
non-unique
+and keep the first occurrence of it.
+
+Default value is 1.
+
 @item keep
-Set the maximum number of consecutive similar frames to ignore before to start 
dropping them.
+For mode 0: Set the maximum number of consecutive similar frames to ignore 
before to start dropping them.
 If the value is 0, the frame is dropped disregarding the
 number of previous sequentially similar frames.
 
diff --git a/libavfilter/vf_mpdecimate.c b/libavfilter/vf_mpdecimate.c
index 00bc6699c3..7523af2e86 100644
--- a/libavfilter/vf_mpdecimate.c
+++ b/libavfilter/vf_mpdecimate.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2003 Rich Felker
  * Copyright (c) 2012 Stefano Sabatini
+ * Copyright (c) 2026 Dawid Stachowiak
  *
  * This file is part of FFmpeg.
  *
@@ -40,19 +41,23 @@ typedef enum {
 
 typedef struct DecimateContext {
     const AVClass *class;
+    int mode;                      ///< 0: drop similar frames, 1: drop 
similar and unique frames
     int lo, hi;                    ///< lower and higher threshold number of 
differences
                                    ///< values for 8x8 blocks
 
     float frac;                    ///< threshold of changed pixels over the 
total fraction
 
-    int max_drop_count;            ///< if positive: maximum number of 
sequential frames to drop
-                                   ///< if negative: minimum number of frames 
between two drops
+    int max_drop_count;            ///< for mode 0: if positive: maximum 
number of sequential frames to drop
+                                   ///< for mode 0: if negative: minimum 
number of frames between two drops
 
     int drop_count;                ///< if positive: number of frames 
sequentially dropped
                                    ///< if negative: number of sequential 
frames which were not dropped
 
-    int max_keep_count;            ///< number of similar frames to ignore 
before to start dropping them
-    int keep_count;                ///< number of similar frames already 
ignored
+    int max_keep_count;            ///< for mode 0: number of similar frames 
to ignore before to start dropping them
+    int keep_count;                ///< for mode 0: number of similar frames 
already ignored
+
+    int min_dup_count;             ///< for mode 1: minimum number of previous 
frames that need to be duplicated to keep frame
+    int dup_count;                 ///< for mode 1: number of duplicated frames
 
     int hsub, vsub;                ///< chroma subsampling values
     AVFrame *ref;                  ///< reference picture
@@ -63,13 +68,16 @@ typedef struct DecimateContext {
 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 
 static const AVOption mpdecimate_options[] = {
-    { "max",  "set the maximum number of consecutive dropped frames 
(positive), or the minimum interval between dropped frames (negative)",
+    { "max",  "for mode 0: set the maximum number of consecutive dropped 
frames (positive), or the minimum interval between dropped frames (negative)",
       OFFSET(max_drop_count), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, 
FLAGS },
-    { "keep", "set the number of similar consecutive frames to be kept before 
starting to drop similar frames",
+    { "keep", "for mode 0: set the number of similar consecutive frames to be 
kept before starting to drop similar frames",
       OFFSET(max_keep_count), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
     { "hi",   "set high dropping threshold", OFFSET(hi), AV_OPT_TYPE_INT, 
{.i64=64*12}, INT_MIN, INT_MAX, FLAGS },
     { "lo",   "set low dropping threshold", OFFSET(lo), AV_OPT_TYPE_INT, 
{.i64=64*5}, INT_MIN, INT_MAX, FLAGS },
     { "frac", "set fraction dropping threshold",  OFFSET(frac), 
AV_OPT_TYPE_FLOAT, {.dbl=0.33}, 0, 1, FLAGS },
+    { "mode", "0: drop similar frames, 1: drop similar and unique frames", 
OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
+    { "min",  "for mode 1: set minimum number of previous frames that need to 
be duplicated to keep current frame",
+      OFFSET(min_dup_count), AV_OPT_TYPE_INT, {.i64=1}, 1, INT_MAX, FLAGS },
     { NULL }
 };
 
@@ -113,16 +121,13 @@ static int diff_planes(AVFilterContext *ctx,
 }
 
 /**
- * Tell if the frame should be decimated, for example if it is no much
- * different with respect to the reference frame ref.
+ * Tell if the frame is different with respect to the reference frame ref.
  */
-static DecimateResult decimate_frame(AVFilterContext *ctx, AVFrame *cur, 
AVFrame *ref)
+static int is_frame_different(AVFilterContext *ctx,
+                              AVFrame *cur, AVFrame *ref)
 {
     DecimateContext *decimate = ctx->priv;
     int plane;
-    int is_similar;
-
-    is_similar = 1;
 
     for (plane = 0; ref->data[plane] && ref->linesize[plane]; plane++) {
         /* use 8x8 SAD even on subsampled planes.  The blocks won't match up 
with
@@ -135,12 +140,23 @@ static DecimateResult decimate_frame(AVFilterContext 
*ctx, AVFrame *cur, AVFrame
         if (diff_planes(ctx,
                         cur->data[plane], cur->linesize[plane],
                         ref->data[plane], ref->linesize[plane],
-                        AV_CEIL_RSHIFT(ref->width,  hsub),
-                        AV_CEIL_RSHIFT(ref->height, vsub))) {
-            is_similar = 0;
-            break;
-        }
+                        AV_CEIL_RSHIFT(ref->width, hsub),
+                        AV_CEIL_RSHIFT(ref->height, vsub)))
+            return 1;
     }
+
+    return 0;
+}
+
+/**
+ * Tell if the frame should be decimated, for example if it is no much
+ * different with respect to the reference frame ref.
+ */
+static DecimateResult decimate_frame(AVFilterContext *ctx, AVFrame *cur, 
AVFrame *ref)
+{
+    DecimateContext *decimate = ctx->priv;
+    int is_similar = is_frame_different(ctx, cur, ref) == 0;
+
     if (!is_similar) {
         return DECIMATE_KEEP_UPDATE;
     }
@@ -169,9 +185,14 @@ static av_cold int init(AVFilterContext *ctx)
     if (!decimate->sad)
         return AVERROR(EINVAL);
 
-    av_log(ctx, AV_LOG_VERBOSE, "max_drop_count:%d hi:%d lo:%d frac:%f\n",
+    if (decimate->mode == 1) {
+        av_log(ctx, AV_LOG_VERBOSE, "min_dup_count:%d hi:%d lo:%d frac:%f\n",
+           decimate->min_dup_count, decimate->hi, decimate->lo,
+           decimate->frac);
+    } else {
+        av_log(ctx, AV_LOG_VERBOSE, "max_drop_count:%d hi:%d lo:%d frac:%f\n",
            decimate->max_drop_count, decimate->hi, decimate->lo, 
decimate->frac);
-
+    }
     return 0;
 }
 
@@ -208,7 +229,7 @@ static int config_input(AVFilterLink *inlink)
     return 0;
 }
 
-static int filter_frame(AVFilterLink *inlink, AVFrame *cur)
+static int filter_frame_mode_0(AVFilterLink *inlink, AVFrame *cur)
 {
     DecimateContext *decimate = inlink->dst->priv;
     AVFilterLink *outlink = inlink->dst->outputs[0];
@@ -257,6 +278,56 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *cur)
     return 0;
 }
 
+static int filter_frame_mode_1(AVFilterLink *inlink, AVFrame *cur)
+{
+    DecimateContext *decimate = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int ret;
+    AVFrame *out = NULL;
+
+    if (!decimate->ref || is_frame_different(inlink->dst, cur, decimate->ref)) 
{
+        AVFrame *ref = av_frame_clone(cur);
+        if (!ref) {
+            av_frame_free(&cur);
+            return AVERROR(ENOMEM);
+        }
+        av_frame_free(&decimate->ref);
+        decimate->ref = ref;
+        decimate->dup_count = 0;
+    } else {
+        decimate->dup_count++;
+    }
+
+    av_log(inlink->dst, AV_LOG_DEBUG,
+           "%s pts:%s pts_time:%s dup_count:%d\n",
+           decimate->dup_count ==
+           decimate->min_dup_count ? "keep" : "drop", av_ts2str(cur->pts),
+           av_ts2timestr(cur->pts, &inlink->time_base),
+           decimate->dup_count);
+
+    if (decimate->dup_count == decimate->min_dup_count) {
+        out = av_frame_clone(decimate->ref);
+        if (!out) {
+            av_frame_free(&cur);
+            return AVERROR(ENOMEM);
+        }
+        ret = ff_filter_frame(outlink, out);
+        if (ret < 0) {
+            av_frame_free(&cur);
+            return ret;
+        }
+    }
+    av_frame_free(&cur);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *cur)
+{
+    DecimateContext *decimate = inlink->dst->priv;
+    return decimate->mode == 0 ? filter_frame_mode_0(inlink, cur) : 
filter_frame_mode_1(inlink, cur);
+}
+
 static const AVFilterPad mpdecimate_inputs[] = {
     {
         .name         = "default",
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index f5f5096f2b..0eb2e7076c 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -475,6 +475,12 @@ fate-filter-mpdecimate-maxdrop-pos: CMD = framecrc -lavfi 
testsrc2=r=1:d=5,fps=1
 FATE_FILTER-$(call FILTERFRAMECRC, TESTSRC2 FPS MPDECIMATE) += 
fate-filter-mpdecimate-maxdrop-neg
 fate-filter-mpdecimate-maxdrop-neg: CMD = framecrc -lavfi 
testsrc2=r=1:d=5,fps=10,mpdecimate=max=-3 -pix_fmt yuv420p
 
+FATE_FILTER-$(call FILTERFRAMECRC, TESTSRC2 FPS MPDECIMATE) += 
fate-filter-mpdecimate-mode-1
+fate-filter-mpdecimate-mode-1: CMD = framecrc -lavfi 
testsrc2=r=4:d=5,fps=5,mpdecimate=mode=1 -pix_fmt yuv420p
+
+FATE_FILTER-$(call FILTERFRAMECRC, TESTSRC2 FPS MPDECIMATE) += 
fate-filter-mpdecimate-mode-1-min-3
+fate-filter-mpdecimate-mode-1-min-3: CMD = framecrc -lavfi 
testsrc2=r=2:d=7,fps=7,mpdecimate=mode=1:min=3 -pix_fmt yuv420p
+
 FATE_FILTER-$(call FILTERFRAMECRC, FPS TESTSRC2) += $(addprefix 
fate-filter-fps-, up up-round-down up-round-up down down-round-down 
down-round-up down-eof-pass start-drop start-fill)
 fate-filter-fps-up: CMD = framecrc -lavfi testsrc2=r=3:d=2,fps=7
 fate-filter-fps-up-round-down: CMD = framecrc -lavfi 
testsrc2=r=3:d=2,fps=7:round=down
diff --git a/tests/ref/fate/filter-mpdecimate-mode-1 
b/tests/ref/fate/filter-mpdecimate-mode-1
new file mode 100644
index 0000000000..f55c09e4be
--- /dev/null
+++ b/tests/ref/fate/filter-mpdecimate-mode-1
@@ -0,0 +1,10 @@
+#tb 0: 1/5
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 320x240
+#sar 0: 1/1
+0,          1,          1,        1,   115200, 0xbd87f5f6
+0,          6,          6,        1,   115200, 0xa0e1f3d6
+0,         11,         11,        1,   115200, 0xc0c5fe56
+0,         16,         16,        1,   115200, 0xba2fccf6
+0,         21,         21,        1,   115200, 0xccefb2df
diff --git a/tests/ref/fate/filter-mpdecimate-mode-1-min-3 
b/tests/ref/fate/filter-mpdecimate-mode-1-min-3
new file mode 100644
index 0000000000..3897f6834e
--- /dev/null
+++ b/tests/ref/fate/filter-mpdecimate-mode-1-min-3
@@ -0,0 +1,12 @@
+#tb 0: 1/7
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 320x240
+#sar 0: 1/1
+0,          0,          0,        1,   115200, 0xeba70ff3
+0,          7,          7,        1,   115200, 0x1dadb3a3
+0,         14,         14,        1,   115200, 0xb8cdb7ac
+0,         21,         21,        1,   115200, 0xb789c3ba
+0,         28,         28,        1,   115200, 0xd1084e35
+0,         35,         35,        1,   115200, 0x7bbb8ede
+0,         42,         42,        1,   115200, 0x939ba4bd

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to