00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00027 #include "libavutil/pixdesc.h"
00028 #include "libavutil/timestamp.h"
00029 #include "libavcodec/dsputil.h"
00030 #include "avfilter.h"
00031 #include "internal.h"
00032 #include "formats.h"
00033 #include "video.h"
00034
00035 typedef struct {
00036 int lo, hi;
00037
00038
00039 float frac;
00040
00041 int max_drop_count;
00042
00043
00044 int drop_count;
00045
00046
00047 int hsub, vsub;
00048 AVFilterBufferRef *ref;
00049 DSPContext dspctx;
00050 AVCodecContext *avctx;
00051 } DecimateContext;
00052
00056 static int diff_planes(AVFilterContext *ctx,
00057 uint8_t *cur, uint8_t *ref, int linesize,
00058 int w, int h)
00059 {
00060 DecimateContext *decimate = ctx->priv;
00061 DSPContext *dspctx = &decimate->dspctx;
00062
00063 int x, y;
00064 int d, c = 0;
00065 int t = (w/16)*(h/16)*decimate->frac;
00066 DCTELEM block[8*8];
00067
00068
00069 for (y = 0; y < h-7; y += 4) {
00070 for (x = 8; x < w-7; x += 4) {
00071 dspctx->diff_pixels(block,
00072 cur+x+y*linesize,
00073 ref+x+y*linesize, linesize);
00074 d = dspctx->sum_abs_dctelem(block);
00075 if (d > decimate->hi)
00076 return 1;
00077 if (d > decimate->lo) {
00078 c++;
00079 if (c > t)
00080 return 1;
00081 }
00082 }
00083 }
00084 return 0;
00085 }
00086
00091 static int decimate_frame(AVFilterContext *ctx,
00092 AVFilterBufferRef *cur, AVFilterBufferRef *ref)
00093 {
00094 DecimateContext *decimate = ctx->priv;
00095 int plane;
00096
00097 if (decimate->max_drop_count > 0 &&
00098 decimate->drop_count >= decimate->max_drop_count)
00099 return 0;
00100 if (decimate->max_drop_count < 0 &&
00101 (decimate->drop_count-1) > decimate->max_drop_count)
00102 return 0;
00103
00104 for (plane = 0; ref->data[plane] && ref->linesize[plane]; plane++) {
00105 int vsub = plane == 1 || plane == 2 ? decimate->vsub : 0;
00106 int hsub = plane == 1 || plane == 2 ? decimate->hsub : 0;
00107 if (diff_planes(ctx,
00108 cur->data[plane], ref->data[plane], ref->linesize[plane],
00109 ref->video->w>>hsub, ref->video->h>>vsub))
00110 return 0;
00111 }
00112
00113 return 1;
00114 }
00115
00116 static av_cold int init(AVFilterContext *ctx, const char *args)
00117 {
00118 DecimateContext *decimate = ctx->priv;
00119
00120
00121 decimate->drop_count = decimate->max_drop_count = 0;
00122 decimate->lo = 64*5;
00123 decimate->hi = 64*12;
00124 decimate->frac = 0.33;
00125
00126 if (args) {
00127 char c1, c2, c3, c4;
00128 int n = sscanf(args, "%d%c%d%c%d%c%f%c",
00129 &decimate->max_drop_count, &c1,
00130 &decimate->hi, &c2, &decimate->lo, &c3,
00131 &decimate->frac, &c4);
00132 if (n != 1 &&
00133 (n != 3 || c1 != ':') &&
00134 (n != 5 || c1 != ':' || c2 != ':') &&
00135 (n != 7 || c1 != ':' || c2 != ':' || c3 != ':')) {
00136 av_log(ctx, AV_LOG_ERROR,
00137 "Invalid syntax for argument '%s': "
00138 "must be in the form 'max:hi:lo:frac'\n", args);
00139 return AVERROR(EINVAL);
00140 }
00141 }
00142
00143 av_log(ctx, AV_LOG_VERBOSE, "max_drop_count:%d hi:%d lo:%d frac:%f\n",
00144 decimate->max_drop_count, decimate->hi, decimate->lo, decimate->frac);
00145
00146 decimate->avctx = avcodec_alloc_context3(NULL);
00147 if (!decimate->avctx)
00148 return AVERROR(ENOMEM);
00149 dsputil_init(&decimate->dspctx, decimate->avctx);
00150
00151 return 0;
00152 }
00153
00154 static av_cold void uninit(AVFilterContext *ctx)
00155 {
00156 DecimateContext *decimate = ctx->priv;
00157 avfilter_unref_bufferp(&decimate->ref);
00158 avcodec_close(decimate->avctx);
00159 av_freep(&decimate->avctx);
00160 }
00161
00162 static int query_formats(AVFilterContext *ctx)
00163 {
00164 static const enum PixelFormat pix_fmts[] = {
00165 PIX_FMT_YUV444P, PIX_FMT_YUV422P,
00166 PIX_FMT_YUV420P, PIX_FMT_YUV411P,
00167 PIX_FMT_YUV410P, PIX_FMT_YUV440P,
00168 PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P,
00169 PIX_FMT_YUVJ420P, PIX_FMT_YUVJ440P,
00170 PIX_FMT_YUVA420P,
00171 PIX_FMT_NONE
00172 };
00173
00174 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00175
00176 return 0;
00177 }
00178
00179 static int config_input(AVFilterLink *inlink)
00180 {
00181 AVFilterContext *ctx = inlink->dst;
00182 DecimateContext *decimate = ctx->priv;
00183 const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
00184 decimate->hsub = pix_desc->log2_chroma_w;
00185 decimate->vsub = pix_desc->log2_chroma_h;
00186
00187 return 0;
00188 }
00189
00190 static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref) { return 0; }
00191
00192 static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { return 0; }
00193
00194 static int end_frame(AVFilterLink *inlink)
00195 {
00196 DecimateContext *decimate = inlink->dst->priv;
00197 AVFilterBufferRef *cur = inlink->cur_buf;
00198 AVFilterLink *outlink = inlink->dst->outputs[0];
00199 int ret;
00200
00201 if (decimate->ref && decimate_frame(inlink->dst, cur, decimate->ref)) {
00202 decimate->drop_count = FFMAX(1, decimate->drop_count+1);
00203 } else {
00204 avfilter_unref_buffer(decimate->ref);
00205 decimate->ref = cur;
00206 inlink->cur_buf = NULL;
00207 decimate->drop_count = FFMIN(-1, decimate->drop_count-1);
00208
00209 if ((ret = ff_start_frame(outlink,
00210 avfilter_ref_buffer(cur, ~AV_PERM_WRITE)) < 0) ||
00211 (ret = ff_draw_slice(outlink, 0, inlink->h, 1)) < 0 ||
00212 (ret = ff_end_frame(outlink)) < 0)
00213 return ret;
00214 }
00215
00216 av_log(inlink->dst, AV_LOG_DEBUG,
00217 "%s pts:%s pts_time:%s drop_count:%d\n",
00218 decimate->drop_count > 0 ? "drop" : "keep",
00219 av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base),
00220 decimate->drop_count);
00221
00222 return 0;
00223 }
00224
00225 static int request_frame(AVFilterLink *outlink)
00226 {
00227 DecimateContext *decimate = outlink->src->priv;
00228 AVFilterLink *inlink = outlink->src->inputs[0];
00229 int ret;
00230
00231 do {
00232 ret = ff_request_frame(inlink);
00233 } while (decimate->drop_count > 0 && ret >= 0);
00234
00235 return ret;
00236 }
00237
00238 AVFilter avfilter_vf_decimate = {
00239 .name = "decimate",
00240 .description = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."),
00241 .init = init,
00242 .uninit = uninit,
00243
00244 .priv_size = sizeof(DecimateContext),
00245 .query_formats = query_formats,
00246
00247 .inputs = (const AVFilterPad[]) {
00248 {
00249 .name = "default",
00250 .type = AVMEDIA_TYPE_VIDEO,
00251 .get_video_buffer = ff_null_get_video_buffer,
00252 .config_props = config_input,
00253 .start_frame = start_frame,
00254 .draw_slice = draw_slice,
00255 .end_frame = end_frame,
00256 .min_perms = AV_PERM_READ | AV_PERM_PRESERVE,
00257 },
00258 { .name = NULL }
00259 },
00260 .outputs = (const AVFilterPad[]) {
00261 {
00262 .name = "default",
00263 .type = AVMEDIA_TYPE_VIDEO,
00264 .request_frame = request_frame,
00265 },
00266 { .name = NULL }
00267 },
00268 };