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 AVPixelFormat pix_fmts[] = {
00165 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P,
00166 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
00167 AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P,
00168 AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P,
00169 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ440P,
00170 AV_PIX_FMT_YUVA420P,
00171 AV_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_desc_get(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 filter_frame(AVFilterLink *inlink, AVFilterBufferRef *cur)
00191 {
00192 DecimateContext *decimate = inlink->dst->priv;
00193 AVFilterLink *outlink = inlink->dst->outputs[0];
00194 int ret;
00195
00196 if (decimate->ref && decimate_frame(inlink->dst, cur, decimate->ref)) {
00197 decimate->drop_count = FFMAX(1, decimate->drop_count+1);
00198 } else {
00199 avfilter_unref_buffer(decimate->ref);
00200 decimate->ref = cur;
00201 inlink->cur_buf = NULL;
00202 decimate->drop_count = FFMIN(-1, decimate->drop_count-1);
00203
00204 if (ret = ff_filter_frame(outlink, avfilter_ref_buffer(cur, ~AV_PERM_WRITE)) < 0)
00205 return ret;
00206 }
00207
00208 av_log(inlink->dst, AV_LOG_DEBUG,
00209 "%s pts:%s pts_time:%s drop_count:%d\n",
00210 decimate->drop_count > 0 ? "drop" : "keep",
00211 av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base),
00212 decimate->drop_count);
00213
00214 if (decimate->drop_count > 0)
00215 avfilter_unref_buffer(cur);
00216
00217 return 0;
00218 }
00219
00220 static int request_frame(AVFilterLink *outlink)
00221 {
00222 DecimateContext *decimate = outlink->src->priv;
00223 AVFilterLink *inlink = outlink->src->inputs[0];
00224 int ret;
00225
00226 do {
00227 ret = ff_request_frame(inlink);
00228 } while (decimate->drop_count > 0 && ret >= 0);
00229
00230 return ret;
00231 }
00232
00233 static const AVFilterPad decimate_inputs[] = {
00234 {
00235 .name = "default",
00236 .type = AVMEDIA_TYPE_VIDEO,
00237 .get_video_buffer = ff_null_get_video_buffer,
00238 .config_props = config_input,
00239 .filter_frame = filter_frame,
00240 .min_perms = AV_PERM_READ | AV_PERM_PRESERVE,
00241 },
00242 { NULL }
00243 };
00244
00245 static const AVFilterPad decimate_outputs[] = {
00246 {
00247 .name = "default",
00248 .type = AVMEDIA_TYPE_VIDEO,
00249 .request_frame = request_frame,
00250 },
00251 { NULL }
00252 };
00253
00254 AVFilter avfilter_vf_decimate = {
00255 .name = "decimate",
00256 .description = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."),
00257 .init = init,
00258 .uninit = uninit,
00259
00260 .priv_size = sizeof(DecimateContext),
00261 .query_formats = query_formats,
00262 .inputs = decimate_inputs,
00263 .outputs = decimate_outputs,
00264 };