FFmpeg
Loading...
Searching...
No Matches
vf_mpdecimate.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2003 Rich Felker
3 * Copyright (c) 2012 Stefano Sabatini
4 * Copyright (c) 2026 Dawid Stachowiak
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23/**
24 * @file mpdecimate filter, ported from libmpcodecs/vf_decimate.c by
25 * Rich Felker.
26 */
27
28#include "libavutil/opt.h"
29#include "libavutil/pixdesc.h"
31#include "libavutil/timestamp.h"
32#include "avfilter.h"
33#include "filters.h"
34#include "video.h"
35
36typedef enum {
37 DECIMATE_DROP, ///< similar frame, past keep threshold — drop it
38 DECIMATE_KEEP_UPDATE, ///< keep frame and update reference (frame is different, or first frame)
39 DECIMATE_KEEP_NO_UPDATE,///< keep frame without updating reference (similar frame under keep threshold, or forced keep due to max_drop_count)
41
42typedef struct DecimateContext {
43 const AVClass *class;
44 int mode; ///< 0: drop similar frames, 1: drop similar and unique frames
45 int lo, hi; ///< lower and higher threshold number of differences
46 ///< values for 8x8 blocks
47
48 float frac; ///< threshold of changed pixels over the total fraction
49
50 int max_drop_count; ///< for mode 0: if positive: maximum number of sequential frames to drop
51 ///< for mode 0: if negative: minimum number of frames between two drops
52
53 int drop_count; ///< if positive: number of frames sequentially dropped
54 ///< if negative: number of sequential frames which were not dropped
55
56 int max_keep_count; ///< for mode 0: number of similar frames to ignore before to start dropping them
57 int keep_count; ///< for mode 0: number of similar frames already ignored
58
59 int min_dup_count; ///< for mode 1: minimum number of previous frames that need to be duplicated to keep frame
60 int dup_count; ///< for mode 1: number of duplicated frames
61
62 int hsub, vsub; ///< chroma subsampling values
63 AVFrame *ref; ///< reference picture
64 av_pixelutils_sad_fn sad; ///< sum of absolute difference function
66
67#define OFFSET(x) offsetof(DecimateContext, x)
68#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
69
70static const AVOption mpdecimate_options[] = {
71 { "max", "for mode 0: set the maximum number of consecutive dropped frames (positive), or the minimum interval between dropped frames (negative)",
72 OFFSET(max_drop_count), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS },
73 { "keep", "for mode 0: set the number of similar consecutive frames to be kept before starting to drop similar frames",
74 OFFSET(max_keep_count), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
75 { "hi", "set high dropping threshold", OFFSET(hi), AV_OPT_TYPE_INT, {.i64=64*12}, INT_MIN, INT_MAX, FLAGS },
76 { "lo", "set low dropping threshold", OFFSET(lo), AV_OPT_TYPE_INT, {.i64=64*5}, INT_MIN, INT_MAX, FLAGS },
77 { "frac", "set fraction dropping threshold", OFFSET(frac), AV_OPT_TYPE_FLOAT, {.dbl=0.33}, 0, 1, FLAGS },
78 { "mode", "0: drop similar frames, 1: drop similar and unique frames", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
79 { "min", "for mode 1: set minimum number of previous frames that need to be duplicated to keep current frame",
80 OFFSET(min_dup_count), AV_OPT_TYPE_INT, {.i64=1}, 1, INT_MAX, FLAGS },
81 { NULL }
82};
83
85
86/**
87 * Return 1 if the two planes are different, 0 otherwise.
88 */
90 uint8_t *cur, int cur_linesize,
91 uint8_t *ref, int ref_linesize,
92 int w, int h)
93{
94 DecimateContext *decimate = ctx->priv;
95
96 int x, y;
97 int d, c = 0;
98 int t = (w/16)*(h/16)*decimate->frac;
99
100 /* compute difference for blocks of 8x8 bytes */
101 for (y = 0; y < h-7; y += 4) {
102 for (x = 8; x < w-7; x += 4) {
103 d = decimate->sad(cur + y*cur_linesize + x, cur_linesize,
104 ref + y*ref_linesize + x, ref_linesize);
105 if (d > decimate->hi) {
106 av_log(ctx, AV_LOG_DEBUG, "%d>=hi ", d);
107 return 1;
108 }
109 if (d > decimate->lo) {
110 c++;
111 if (c > t) {
112 av_log(ctx, AV_LOG_DEBUG, "lo:%d>=%d ", c, t);
113 return 1;
114 }
115 }
116 }
117 }
118
119 av_log(ctx, AV_LOG_DEBUG, "lo:%d<%d ", c, t);
120 return 0;
121}
122
123/**
124 * Tell if the frame is different with respect to the reference frame ref.
125 */
127 AVFrame *cur, AVFrame *ref)
128{
129 DecimateContext *decimate = ctx->priv;
130 int plane;
131
132 for (plane = 0; ref->data[plane] && ref->linesize[plane]; plane++) {
133 /* use 8x8 SAD even on subsampled planes. The blocks won't match up with
134 * luma blocks, but hopefully nobody is depending on this to catch
135 * localized chroma changes that wouldn't exceed the thresholds when
136 * diluted by using what's effectively a larger block size.
137 */
138 int vsub = plane == 1 || plane == 2 ? decimate->vsub : 0;
139 int hsub = plane == 1 || plane == 2 ? decimate->hsub : 0;
140 if (diff_planes(ctx,
141 cur->data[plane], cur->linesize[plane],
142 ref->data[plane], ref->linesize[plane],
143 AV_CEIL_RSHIFT(ref->width, hsub),
144 AV_CEIL_RSHIFT(ref->height, vsub)))
145 return 1;
146 }
147
148 return 0;
149}
150
151/**
152 * Tell if the frame should be decimated, for example if it is no much
153 * different with respect to the reference frame ref.
154 */
156{
157 DecimateContext *decimate = ctx->priv;
158 int is_similar = is_frame_different(ctx, cur, ref) == 0;
159
160 if (!is_similar) {
162 }
163 /* Frame is similar - check if we must keep it due to drop limits */
164 if (decimate->max_drop_count > 0 &&
165 decimate->drop_count >= decimate->max_drop_count)
167 if (decimate->max_drop_count < 0 &&
168 (decimate->drop_count - 1) > decimate->max_drop_count)
170
171 /* Frame is similar - check if we must keep it due to keep option */
172 if (decimate->max_keep_count > 0 && decimate->keep_count > -1 &&
173 decimate->keep_count < decimate->max_keep_count) {
174 decimate->keep_count++;
176 }
177 return DECIMATE_DROP;
178}
179
181{
182 DecimateContext *decimate = ctx->priv;
183
184 decimate->sad = av_pixelutils_get_sad_fn(3, 3, 0, ctx); // 8x8, not aligned on blocksize
185 if (!decimate->sad)
186 return AVERROR(EINVAL);
187
188 if (decimate->mode == 1) {
189 av_log(ctx, AV_LOG_VERBOSE, "min_dup_count:%d hi:%d lo:%d frac:%f\n",
190 decimate->min_dup_count, decimate->hi, decimate->lo,
191 decimate->frac);
192 } else {
193 av_log(ctx, AV_LOG_VERBOSE, "max_drop_count:%d hi:%d lo:%d frac:%f\n",
194 decimate->max_drop_count, decimate->hi, decimate->lo, decimate->frac);
195 }
196 return 0;
197}
198
200{
201 DecimateContext *decimate = ctx->priv;
202 av_frame_free(&decimate->ref);
203}
204
220
221static int config_input(AVFilterLink *inlink)
222{
223 AVFilterContext *ctx = inlink->dst;
224 DecimateContext *decimate = ctx->priv;
225 const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
226 decimate->hsub = pix_desc->log2_chroma_w;
227 decimate->vsub = pix_desc->log2_chroma_h;
228
229 return 0;
230}
231
232static int filter_frame_mode_0(AVFilterLink *inlink, AVFrame *cur)
233{
234 DecimateContext *decimate = inlink->dst->priv;
235 AVFilterLink *outlink = inlink->dst->outputs[0];
236 AVFrame *out = NULL;
237 int ret;
238 DecimateResult result = decimate->ref ? decimate_frame(inlink->dst, cur, decimate->ref) : DECIMATE_KEEP_UPDATE;
239
240 switch (result) {
241 case DECIMATE_DROP:
242 decimate->drop_count = FFMAX(1, decimate->drop_count+1);
243 decimate->keep_count = -1;
244 break;
246 decimate->drop_count = FFMIN(-1, decimate->drop_count-1);
247 out = cur;
248 break;
250 out = av_frame_clone(cur);
251 if (!out) {
252 av_frame_free(&cur);
253 return AVERROR(ENOMEM);
254 }
255 av_frame_free(&decimate->ref);
256 decimate->ref = cur;
257 decimate->drop_count = FFMIN(-1, decimate->drop_count-1);
258 decimate->keep_count = 0;
259 break;
260 }
261
262 av_log(inlink->dst, AV_LOG_DEBUG,
263 "%s pts:%s pts_time:%s drop_count:%d keep_count:%d\n",
264 result == DECIMATE_DROP ? "drop" : "keep",
265 av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base),
266 decimate->drop_count,
267 decimate->keep_count);
268
269 if (result == DECIMATE_DROP) {
270 av_frame_free(&cur);
271 return 0;
272 }
273
274 ret = ff_filter_frame(outlink, out);
275 if (ret < 0)
276 return ret;
277
278 return 0;
279}
280
281static int filter_frame_mode_1(AVFilterLink *inlink, AVFrame *cur)
282{
283 DecimateContext *decimate = inlink->dst->priv;
284 AVFilterLink *outlink = inlink->dst->outputs[0];
285 int ret;
286 AVFrame *out = NULL;
287
288 if (!decimate->ref || is_frame_different(inlink->dst, cur, decimate->ref)) {
289 AVFrame *ref = av_frame_clone(cur);
290 if (!ref) {
291 av_frame_free(&cur);
292 return AVERROR(ENOMEM);
293 }
294 av_frame_free(&decimate->ref);
295 decimate->ref = ref;
296 decimate->dup_count = 0;
297 } else {
298 decimate->dup_count++;
299 }
300
301 av_log(inlink->dst, AV_LOG_DEBUG,
302 "%s pts:%s pts_time:%s dup_count:%d\n",
303 decimate->dup_count ==
304 decimate->min_dup_count ? "keep" : "drop", av_ts2str(cur->pts),
305 av_ts2timestr(cur->pts, &inlink->time_base),
306 decimate->dup_count);
307
308 if (decimate->dup_count == decimate->min_dup_count) {
309 out = av_frame_clone(decimate->ref);
310 if (!out) {
311 av_frame_free(&cur);
312 return AVERROR(ENOMEM);
313 }
314 ret = ff_filter_frame(outlink, out);
315 if (ret < 0) {
316 av_frame_free(&cur);
317 return ret;
318 }
319 }
320 av_frame_free(&cur);
321
322 return 0;
323}
324
325static int filter_frame(AVFilterLink *inlink, AVFrame *cur)
326{
327 DecimateContext *decimate = inlink->dst->priv;
328 return decimate->mode == 0 ? filter_frame_mode_0(inlink, cur) : filter_frame_mode_1(inlink, cur);
329}
330
332 {
333 .name = "default",
334 .type = AVMEDIA_TYPE_VIDEO,
335 .config_props = config_input,
336 .filter_frame = filter_frame,
337 },
338};
339
341 .p.name = "mpdecimate",
342 .p.description = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."),
343 .p.priv_class = &mpdecimate_class,
344 .init = init,
345 .uninit = uninit,
346 .priv_size = sizeof(DecimateContext),
350};
static int config_input(AVFilterLink *inlink)
const FFFilter ff_vf_mpdecimate
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
Main libavfilter public API header.
#define FLAGS
Definition cmdutils.c:598
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
static int filter_frame(DBEDecodeContext *s, AVFrame *frame)
Definition dolby_e.c:1067
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_FLOAT
Underlying C type is float.
Definition opt.h:270
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
AVFrame * av_frame_clone(const AVFrame *src)
Create a new frame that references the same data as src.
Definition frame.c:483
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
static av_cold void uninit(AVBitStreamFilterContext *ctx)
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FILTER_PIXFMTS_ARRAY(array)
Definition filters.h:244
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
av_pixelutils_sad_fn av_pixelutils_get_sad_fn(int w_bits, int h_bits, int aligned, void *log_ctx)
Get a potentially optimized pointer to a Sum-of-absolute-differences function (see the av_pixelutils_...
Definition pixelutils.c:82
static enum AVPixelFormat pix_fmts[]
Definition libkvazaar.c:296
uint8_t w
Definition llvidencdsp.c:39
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
AVOptions.
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
int(* av_pixelutils_sad_fn)(const uint8_t *src1, ptrdiff_t stride1, const uint8_t *src2, ptrdiff_t stride2)
Sum of abs(src1[x] - src2[x])
Definition pixelutils.h:28
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_YUV440P
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition pixfmt.h:106
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:77
@ AV_PIX_FMT_YUVA420P
planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
Definition pixfmt.h:108
@ AV_PIX_FMT_YUVJ440P
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
Definition pixfmt.h:107
@ AV_PIX_FMT_YUV410P
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition pixfmt.h:79
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition pixfmt.h:80
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
@ AV_PIX_FMT_YUVA444P
planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
Definition pixfmt.h:174
@ AV_PIX_FMT_YUVJ422P
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition pixfmt.h:86
@ AV_PIX_FMT_YUVA422P
planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
Definition pixfmt.h:173
@ AV_PIX_FMT_GBRP
planar GBR 4:4:4 24bpp
Definition pixfmt.h:165
@ AV_PIX_FMT_YUVJ444P
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition pixfmt.h:87
@ AV_PIX_FMT_YUVJ420P
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition pixfmt.h:85
An instance of a filter.
Definition avfilter.h:273
void * priv
private data for use by the filter
Definition avfilter.h:288
AVFilterLink ** outputs
array of pointers to output links
Definition avfilter.h:285
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition frame.h:574
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition frame.h:517
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition pixdesc.h:80
uint8_t log2_chroma_h
Amount to shift the luma height right to find the chroma height.
Definition pixdesc.h:89
float frac
threshold of changed pixels over the total fraction
AVFrame * ref
reference picture
int max_drop_count
for mode 0: if positive: maximum number of sequential frames to drop for mode 0: if negative: minimum...
int dup_count
for mode 1: number of duplicated frames
int min_dup_count
for mode 1: minimum number of previous frames that need to be duplicated to keep frame
int drop_count
if positive: number of frames sequentially dropped if negative: number of sequential frames which wer...
int mode
0: drop similar frames, 1: drop similar and unique frames
av_pixelutils_sad_fn sad
sum of absolute difference function
int max_keep_count
for mode 0: number of similar frames to ignore before to start dropping them
int hi
lower and higher threshold number of differences values for 8x8 blocks
int keep_count
for mode 0: number of similar frames already ignored
int vsub
chroma subsampling values
Definition vf_decimate.c:49
Definition swscale.c:71
#define av_log(a,...)
static int ref[MAX_W *MAX_W]
static FILE * out
Definition movenc.c:55
static AVFormatContext * ctx
Definition movenc.c:49
timestamp utils, mostly useful for debugging/logging purposes
#define av_ts2str(ts)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition timestamp.h:54
#define av_ts2timestr(ts, tb)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition timestamp.h:83
static void hsub(htype *dst, const htype *src, int bins)
Definition vf_median.c:74
static const AVOption mpdecimate_options[]
DecimateResult
@ DECIMATE_KEEP_NO_UPDATE
keep frame without updating reference (similar frame under keep threshold, or forced keep due to max_...
@ DECIMATE_KEEP_UPDATE
keep frame and update reference (frame is different, or first frame)
@ DECIMATE_DROP
similar frame, past keep threshold — drop it
static int filter_frame(AVFilterLink *inlink, AVFrame *cur)
static int config_input(AVFilterLink *inlink)
static int filter_frame_mode_1(AVFilterLink *inlink, AVFrame *cur)
static const AVFilterPad mpdecimate_inputs[]
static int filter_frame_mode_0(AVFilterLink *inlink, AVFrame *cur)
static int diff_planes(AVFilterContext *ctx, uint8_t *cur, int cur_linesize, uint8_t *ref, int ref_linesize, int w, int h)
Return 1 if the two planes are different, 0 otherwise.
static av_cold void uninit(AVFilterContext *ctx)
static DecimateResult decimate_frame(AVFilterContext *ctx, AVFrame *cur, AVFrame *ref)
Tell if the frame should be decimated, for example if it is no much different with respect to the ref...
#define OFFSET(x)
static int is_frame_different(AVFilterContext *ctx, AVFrame *cur, AVFrame *ref)
Tell if the frame is different with respect to the reference frame ref.
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition video.c:37
static double c[64]