This filter is based on the MPlayer decimate filter by Rich Felker.
Stefano Sabatini authored on 2012/03/17 23:49:51... | ... |
@@ -1889,6 +1889,7 @@ blackframe_filter_deps="gpl" |
1889 | 1889 |
boxblur_filter_deps="gpl" |
1890 | 1890 |
colormatrix_filter_deps="gpl" |
1891 | 1891 |
cropdetect_filter_deps="gpl" |
1892 |
+decimate_filter_deps="gpl" |
|
1892 | 1893 |
delogo_filter_deps="gpl" |
1893 | 1894 |
deshake_filter_deps="avcodec" |
1894 | 1895 |
drawtext_filter_deps="libfreetype" |
... | ... |
@@ -1539,6 +1539,43 @@ indicates never reset and return the largest area encountered during |
1539 | 1539 |
playback. |
1540 | 1540 |
@end table |
1541 | 1541 |
|
1542 |
+@section decimate |
|
1543 |
+ |
|
1544 |
+This filter drops frames that do not differ greatly from the previous |
|
1545 |
+frame in order to reduce framerate. The main use of this filter is |
|
1546 |
+for very-low-bitrate encoding (e.g. streaming over dialup modem), but |
|
1547 |
+it could in theory be used for fixing movies that were |
|
1548 |
+inverse-telecined incorrectly. |
|
1549 |
+ |
|
1550 |
+It accepts the following parameters: |
|
1551 |
+@var{max}:@var{hi}:@var{lo}:@var{frac}. |
|
1552 |
+ |
|
1553 |
+@table @option |
|
1554 |
+ |
|
1555 |
+@item max |
|
1556 |
+Set the maximum number of consecutive frames which can be dropped (if |
|
1557 |
+positive), or the minimum interval between dropped frames (if |
|
1558 |
+negative). If the value is 0, the frame is dropped unregarding the |
|
1559 |
+number of previous sequentially dropped frames. |
|
1560 |
+ |
|
1561 |
+Default value is 0. |
|
1562 |
+ |
|
1563 |
+@item hi, lo, frac |
|
1564 |
+Set the dropping threshold values. |
|
1565 |
+ |
|
1566 |
+Values for @var{hi} and @var{lo} are for 8x8 pixel blocks and |
|
1567 |
+represent actual pixel value differences, so a threshold of 64 |
|
1568 |
+corresponds to 1 unit of difference for each pixel, or the same spread |
|
1569 |
+out differently over the block. |
|
1570 |
+ |
|
1571 |
+A frame is a candidate for dropping if no 8x8 blocks differ by more |
|
1572 |
+than a threshold of @var{hi}, and if no more than @var{frac} blocks (1 |
|
1573 |
+meaning the whole image) differ by more than a threshold of @var{lo}. |
|
1574 |
+ |
|
1575 |
+Default value for @var{hi} is 64*12, default value for @var{lo} is |
|
1576 |
+64*5, and default value for @var{frac} is 0.33. |
|
1577 |
+@end table |
|
1578 |
+ |
|
1542 | 1579 |
@section delogo |
1543 | 1580 |
|
1544 | 1581 |
Suppress a TV station logo by a simple interpolation of the surrounding |
... | ... |
@@ -10,6 +10,7 @@ FFLIBS-$(CONFIG_ACONVERT_FILTER) += swresample |
10 | 10 |
FFLIBS-$(CONFIG_AMOVIE_FILTER) += avformat avcodec |
11 | 11 |
FFLIBS-$(CONFIG_ARESAMPLE_FILTER) += swresample |
12 | 12 |
FFLIBS-$(CONFIG_ATEMPO_FILTER) += avcodec |
13 |
+FFLIBS-$(CONFIG_DECIMATE_FILTER) += avcodec |
|
13 | 14 |
FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec |
14 | 15 |
FFLIBS-$(CONFIG_PAN_FILTER) += swresample |
15 | 16 |
FFLIBS-$(CONFIG_REMOVELOGO_FILTER) += avformat avcodec |
... | ... |
@@ -88,6 +89,7 @@ OBJS-$(CONFIG_COLORMATRIX_FILTER) += vf_colormatrix.o |
88 | 88 |
OBJS-$(CONFIG_COPY_FILTER) += vf_copy.o |
89 | 89 |
OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o |
90 | 90 |
OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o |
91 |
+OBJS-$(CONFIG_DECIMATE_FILTER) += vf_decimate.o |
|
91 | 92 |
OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o |
92 | 93 |
OBJS-$(CONFIG_DESHAKE_FILTER) += vf_deshake.o |
93 | 94 |
OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o |
... | ... |
@@ -78,6 +78,7 @@ void avfilter_register_all(void) |
78 | 78 |
REGISTER_FILTER (COPY, copy, vf); |
79 | 79 |
REGISTER_FILTER (CROP, crop, vf); |
80 | 80 |
REGISTER_FILTER (CROPDETECT, cropdetect, vf); |
81 |
+ REGISTER_FILTER (DECIMATE, decimate, vf); |
|
81 | 82 |
REGISTER_FILTER (DELOGO, delogo, vf); |
82 | 83 |
REGISTER_FILTER (DESHAKE, deshake, vf); |
83 | 84 |
REGISTER_FILTER (DRAWBOX, drawbox, vf); |
... | ... |
@@ -29,8 +29,8 @@ |
29 | 29 |
#include "libavutil/avutil.h" |
30 | 30 |
|
31 | 31 |
#define LIBAVFILTER_VERSION_MAJOR 3 |
32 |
-#define LIBAVFILTER_VERSION_MINOR 14 |
|
33 |
-#define LIBAVFILTER_VERSION_MICRO 101 |
|
32 |
+#define LIBAVFILTER_VERSION_MINOR 15 |
|
33 |
+#define LIBAVFILTER_VERSION_MICRO 100 |
|
34 | 34 |
|
35 | 35 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
36 | 36 |
LIBAVFILTER_VERSION_MINOR, \ |
37 | 37 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,268 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2003 Rich Felker |
|
2 |
+ * Copyright (c) 2012 Stefano Sabatini |
|
3 |
+ * |
|
4 |
+ * This file is part of FFmpeg. |
|
5 |
+ * |
|
6 |
+ * FFmpeg is free software; you can redistribute it and/or modify |
|
7 |
+ * it under the terms of the GNU General Public License as published by |
|
8 |
+ * the Free Software Foundation; either version 2 of the License, or |
|
9 |
+ * (at your option) any later version. |
|
10 |
+ * |
|
11 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
+ * GNU General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU General Public License along |
|
17 |
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc., |
|
18 |
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|
19 |
+ */ |
|
20 |
+ |
|
21 |
+/** |
|
22 |
+ * @file decimate filter, ported from libmpcodecs/vf_decimate.c by |
|
23 |
+ * Rich Felker. |
|
24 |
+ */ |
|
25 |
+ |
|
26 |
+#include "libavutil/pixdesc.h" |
|
27 |
+#include "libavutil/timestamp.h" |
|
28 |
+#include "libavcodec/dsputil.h" |
|
29 |
+#include "avfilter.h" |
|
30 |
+#include "internal.h" |
|
31 |
+#include "formats.h" |
|
32 |
+#include "video.h" |
|
33 |
+ |
|
34 |
+typedef struct { |
|
35 |
+ int lo, hi; ///< lower and higher threshold number of differences |
|
36 |
+ ///< values for 8x8 blocks |
|
37 |
+ |
|
38 |
+ float frac; ///< threshold of changed pixels over the total fraction |
|
39 |
+ |
|
40 |
+ int max_drop_count; ///< if positive: maximum number of sequential frames to drop |
|
41 |
+ ///< if negative: minimum number of frames between two drops |
|
42 |
+ |
|
43 |
+ int drop_count; ///< if positive: number of frames sequentially dropped |
|
44 |
+ ///< if negative: number of sequential frames which were not dropped |
|
45 |
+ |
|
46 |
+ int hsub, vsub; ///< chroma subsampling values |
|
47 |
+ AVFilterBufferRef *ref; ///< reference picture |
|
48 |
+ DSPContext dspctx; ///< context providing optimized diff routines |
|
49 |
+ AVCodecContext *avctx; ///< codec context required for the DSPContext |
|
50 |
+} DecimateContext; |
|
51 |
+ |
|
52 |
+/** |
|
53 |
+ * Return 1 if the two planes are different, 0 otherwise. |
|
54 |
+ */ |
|
55 |
+static int diff_planes(AVFilterContext *ctx, |
|
56 |
+ uint8_t *cur, uint8_t *ref, int linesize, |
|
57 |
+ int w, int h) |
|
58 |
+{ |
|
59 |
+ DecimateContext *decimate = ctx->priv; |
|
60 |
+ DSPContext *dspctx = &decimate->dspctx; |
|
61 |
+ |
|
62 |
+ int x, y; |
|
63 |
+ int d, c = 0; |
|
64 |
+ int t = (w/16)*(h/16)*decimate->frac; |
|
65 |
+ DCTELEM block[8*8]; |
|
66 |
+ |
|
67 |
+ /* compute difference for blocks of 8x8 bytes */ |
|
68 |
+ for (y = 0; y < h-7; y += 4) { |
|
69 |
+ for (x = 8; x < w-7; x += 4) { |
|
70 |
+ dspctx->diff_pixels(block, |
|
71 |
+ cur+x+y*linesize, |
|
72 |
+ ref+x+y*linesize, linesize); |
|
73 |
+ d = dspctx->sum_abs_dctelem(block); |
|
74 |
+ if (d > decimate->hi) |
|
75 |
+ return 1; |
|
76 |
+ if (d > decimate->lo) { |
|
77 |
+ c++; |
|
78 |
+ if (c > t) |
|
79 |
+ return 1; |
|
80 |
+ } |
|
81 |
+ } |
|
82 |
+ } |
|
83 |
+ return 0; |
|
84 |
+} |
|
85 |
+ |
|
86 |
+/** |
|
87 |
+ * Tell if the frame should be decimated, for example if it is no much |
|
88 |
+ * different with respect to the reference frame ref. |
|
89 |
+ */ |
|
90 |
+static int decimate_frame(AVFilterContext *ctx, |
|
91 |
+ AVFilterBufferRef *cur, AVFilterBufferRef *ref) |
|
92 |
+{ |
|
93 |
+ DecimateContext *decimate = ctx->priv; |
|
94 |
+ int plane; |
|
95 |
+ |
|
96 |
+ if (decimate->max_drop_count > 0 && |
|
97 |
+ decimate->drop_count >= decimate->max_drop_count) |
|
98 |
+ return 0; |
|
99 |
+ if (decimate->max_drop_count < 0 && |
|
100 |
+ (decimate->drop_count-1) > decimate->max_drop_count) |
|
101 |
+ return 0; |
|
102 |
+ |
|
103 |
+ for (plane = 0; ref->data[plane] && ref->linesize[plane]; plane++) { |
|
104 |
+ int vsub = plane == 1 || plane == 2 ? decimate->vsub : 0; |
|
105 |
+ int hsub = plane == 1 || plane == 2 ? decimate->hsub : 0; |
|
106 |
+ if (diff_planes(ctx, |
|
107 |
+ cur->data[plane], ref->data[plane], ref->linesize[plane], |
|
108 |
+ ref->video->w>>hsub, ref->video->h>>vsub)) |
|
109 |
+ return 0; |
|
110 |
+ } |
|
111 |
+ |
|
112 |
+ return 1; |
|
113 |
+} |
|
114 |
+ |
|
115 |
+static av_cold int init(AVFilterContext *ctx, const char *args) |
|
116 |
+{ |
|
117 |
+ DecimateContext *decimate = ctx->priv; |
|
118 |
+ |
|
119 |
+ /* set default values */ |
|
120 |
+ decimate->drop_count = decimate->max_drop_count = 0; |
|
121 |
+ decimate->lo = 64*5; |
|
122 |
+ decimate->hi = 64*12; |
|
123 |
+ decimate->frac = 0.33; |
|
124 |
+ |
|
125 |
+ if (args) { |
|
126 |
+ char c1, c2, c3, c4; |
|
127 |
+ int n = sscanf(args, "%d%c%d%c%d%c%f%c", |
|
128 |
+ &decimate->max_drop_count, &c1, |
|
129 |
+ &decimate->hi, &c2, &decimate->lo, &c3, |
|
130 |
+ &decimate->frac, &c4); |
|
131 |
+ if (n != 1 && |
|
132 |
+ (n != 3 || c1 != ':') && |
|
133 |
+ (n != 5 || c1 != ':' || c2 != ':') && |
|
134 |
+ (n != 7 || c1 != ':' || c2 != ':' || c3 != ':')) { |
|
135 |
+ av_log(ctx, AV_LOG_ERROR, |
|
136 |
+ "Invalid syntax for argument '%s': " |
|
137 |
+ "must be in the form 'max:hi:lo:frac'\n", args); |
|
138 |
+ return AVERROR(EINVAL); |
|
139 |
+ } |
|
140 |
+ } |
|
141 |
+ |
|
142 |
+ av_log(ctx, AV_LOG_VERBOSE, "max_drop_count:%d hi:%d lo:%d frac:%f\n", |
|
143 |
+ decimate->max_drop_count, decimate->hi, decimate->lo, decimate->frac); |
|
144 |
+ |
|
145 |
+ decimate->avctx = avcodec_alloc_context3(NULL); |
|
146 |
+ if (!decimate->avctx) |
|
147 |
+ return AVERROR(ENOMEM); |
|
148 |
+ dsputil_init(&decimate->dspctx, decimate->avctx); |
|
149 |
+ |
|
150 |
+ return 0; |
|
151 |
+} |
|
152 |
+ |
|
153 |
+static av_cold void uninit(AVFilterContext *ctx) |
|
154 |
+{ |
|
155 |
+ DecimateContext *decimate = ctx->priv; |
|
156 |
+ avfilter_unref_bufferp(&decimate->ref); |
|
157 |
+ avcodec_close(decimate->avctx); |
|
158 |
+ av_freep(&decimate->avctx); |
|
159 |
+} |
|
160 |
+ |
|
161 |
+static int query_formats(AVFilterContext *ctx) |
|
162 |
+{ |
|
163 |
+ static const enum PixelFormat pix_fmts[] = { |
|
164 |
+ PIX_FMT_YUV444P, PIX_FMT_YUV422P, |
|
165 |
+ PIX_FMT_YUV420P, PIX_FMT_YUV411P, |
|
166 |
+ PIX_FMT_YUV410P, PIX_FMT_YUV440P, |
|
167 |
+ PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, |
|
168 |
+ PIX_FMT_YUVJ420P, PIX_FMT_YUVJ440P, |
|
169 |
+ PIX_FMT_YUVA420P, |
|
170 |
+ PIX_FMT_NONE |
|
171 |
+ }; |
|
172 |
+ |
|
173 |
+ ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); |
|
174 |
+ |
|
175 |
+ return 0; |
|
176 |
+} |
|
177 |
+ |
|
178 |
+static int config_input(AVFilterLink *inlink) |
|
179 |
+{ |
|
180 |
+ AVFilterContext *ctx = inlink->dst; |
|
181 |
+ DecimateContext *decimate = ctx->priv; |
|
182 |
+ const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format]; |
|
183 |
+ decimate->hsub = pix_desc->log2_chroma_w; |
|
184 |
+ decimate->vsub = pix_desc->log2_chroma_h; |
|
185 |
+ |
|
186 |
+ return 0; |
|
187 |
+} |
|
188 |
+ |
|
189 |
+static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref) { return 0; } |
|
190 |
+ |
|
191 |
+static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { return 0; } |
|
192 |
+ |
|
193 |
+static int end_frame(AVFilterLink *inlink) |
|
194 |
+{ |
|
195 |
+ DecimateContext *decimate = inlink->dst->priv; |
|
196 |
+ AVFilterBufferRef *cur = inlink->cur_buf; |
|
197 |
+ AVFilterLink *outlink = inlink->dst->outputs[0]; |
|
198 |
+ int ret; |
|
199 |
+ |
|
200 |
+ if (decimate->ref && decimate_frame(inlink->dst, cur, decimate->ref)) { |
|
201 |
+ decimate->drop_count = FFMAX(1, decimate->drop_count+1); |
|
202 |
+ } else { |
|
203 |
+ avfilter_unref_buffer(decimate->ref); |
|
204 |
+ decimate->ref = cur; |
|
205 |
+ inlink->cur_buf = NULL; |
|
206 |
+ decimate->drop_count = FFMIN(-1, decimate->drop_count-1); |
|
207 |
+ |
|
208 |
+ if ((ret = ff_start_frame(outlink, |
|
209 |
+ avfilter_ref_buffer(cur, ~AV_PERM_WRITE)) < 0) || |
|
210 |
+ (ret = ff_draw_slice(outlink, 0, inlink->h, 1)) < 0 || |
|
211 |
+ (ret = ff_end_frame(outlink)) < 0) |
|
212 |
+ return ret; |
|
213 |
+ } |
|
214 |
+ |
|
215 |
+ av_log(inlink->dst, AV_LOG_DEBUG, |
|
216 |
+ "%s pts:%s pts_time:%s drop_count:%d\n", |
|
217 |
+ decimate->drop_count > 0 ? "drop" : "keep", |
|
218 |
+ av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base), |
|
219 |
+ decimate->drop_count); |
|
220 |
+ |
|
221 |
+ return 0; |
|
222 |
+} |
|
223 |
+ |
|
224 |
+static int request_frame(AVFilterLink *outlink) |
|
225 |
+{ |
|
226 |
+ DecimateContext *decimate = outlink->src->priv; |
|
227 |
+ AVFilterLink *inlink = outlink->src->inputs[0]; |
|
228 |
+ int ret; |
|
229 |
+ |
|
230 |
+ do { |
|
231 |
+ ret = ff_request_frame(inlink); |
|
232 |
+ } while (decimate->drop_count > 0 && ret >= 0); |
|
233 |
+ |
|
234 |
+ return ret; |
|
235 |
+} |
|
236 |
+ |
|
237 |
+AVFilter avfilter_vf_decimate = { |
|
238 |
+ .name = "decimate", |
|
239 |
+ .description = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."), |
|
240 |
+ .init = init, |
|
241 |
+ .uninit = uninit, |
|
242 |
+ |
|
243 |
+ .priv_size = sizeof(DecimateContext), |
|
244 |
+ .query_formats = query_formats, |
|
245 |
+ |
|
246 |
+ .inputs = (const AVFilterPad[]) { |
|
247 |
+ { |
|
248 |
+ .name = "default", |
|
249 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
250 |
+ .get_video_buffer = ff_null_get_video_buffer, |
|
251 |
+ .config_props = config_input, |
|
252 |
+ .start_frame = start_frame, |
|
253 |
+ .draw_slice = draw_slice, |
|
254 |
+ .end_frame = end_frame, |
|
255 |
+ .min_perms = AV_PERM_READ | AV_PERM_PRESERVE, |
|
256 |
+ }, |
|
257 |
+ { .name = NULL } |
|
258 |
+ }, |
|
259 |
+ .outputs = (const AVFilterPad[]) { |
|
260 |
+ { |
|
261 |
+ .name = "default", |
|
262 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
263 |
+ .request_frame = request_frame, |
|
264 |
+ }, |
|
265 |
+ { .name = NULL } |
|
266 |
+ }, |
|
267 |
+}; |