00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <stdio.h>
00020 #include <stdlib.h>
00021 #include <string.h>
00022
00023 #include "config.h"
00024 #include "mp_msg.h"
00025
00026 #include "img_format.h"
00027 #include "mp_image.h"
00028 #include "vf.h"
00029
00030 #include "libvo/fastmemcpy.h"
00031
00032 struct metrics {
00033 int even;
00034 int odd;
00035 int noise;
00036 int temp;
00037 };
00038
00039 struct vf_priv_s {
00040 int frame;
00041 int drop, lastdrop;
00042 struct metrics pm;
00043 int thres[5];
00044 int inframes, outframes;
00045 int mode;
00046 int (*analyze)(struct vf_priv_s *, mp_image_t *, mp_image_t *);
00047 int needread;
00048 };
00049
00050 #define COMPE(a,b,e) (abs((a)-(b)) < (((a)+(b))>>(e)))
00051 #define COMPARABLE(a,b) COMPE((a),(b),2)
00052 #define VERYCLOSE(a,b) COMPE((a),(b),3)
00053
00054 #define OUTER_TC_NBHD(s) ( \
00055 COMPARABLE((s)[-1].m.even,(s)[-1].m.odd) && \
00056 COMPARABLE((s)[1].m.even,(s)[0].m.odd) && \
00057 COMPARABLE((s)[2].m.even,(s)[1].m.odd) && \
00058 COMPARABLE((s)[-1].m.noise,(s)[0].m.temp) && \
00059 COMPARABLE((s)[2].m.noise,(s)[2].m.temp) )
00060
00061 #define INNER_TC_NBHD(s,l,h) ( \
00062 COMPARABLE((s)[0].m.even,(l)) && \
00063 COMPARABLE((s)[2].m.odd,(l)) && ( \
00064 COMPARABLE((s)[0].m.noise,(h)) || \
00065 COMPARABLE((s)[1].m.noise,(h)) ) )
00066
00067 enum {
00068 TC_DROP,
00069 TC_PROG,
00070 TC_IL1,
00071 TC_IL2
00072 };
00073
00074 static void block_diffs(struct metrics *m, unsigned char *old, unsigned char *new, int os, int ns)
00075 {
00076 int x, y, even=0, odd=0, noise, temp;
00077 unsigned char *oldp, *newp;
00078 m->noise = m->temp = 0;
00079 for (x = 8; x; x--) {
00080 oldp = old++;
00081 newp = new++;
00082 noise = temp = 0;
00083 for (y = 4; y; y--) {
00084 even += abs(newp[0]-oldp[0]);
00085 odd += abs(newp[ns]-oldp[os]);
00086 noise += newp[ns]-newp[0];
00087 temp += oldp[os]-newp[0];
00088 oldp += os<<1;
00089 newp += ns<<1;
00090 }
00091 m->noise += abs(noise);
00092 m->temp += abs(temp);
00093 }
00094 m->even = even;
00095 m->odd = odd;
00096 }
00097
00098 static void diff_planes(struct metrics *m, unsigned char *old, unsigned char *new, int w, int h, int os, int ns)
00099 {
00100 int x, y, me=0, mo=0, mn=0, mt=0;
00101 struct metrics l;
00102 for (y = 0; y < h-7; y += 8) {
00103 for (x = 0; x < w-7; x += 8) {
00104 block_diffs(&l, old+x+y*os, new+x+y*ns, os, ns);
00105 if (l.even > me) me = l.even;
00106 if (l.odd > mo) mo = l.odd;
00107 if (l.noise > mn) mn = l.noise;
00108 if (l.temp > mt) mt = l.temp;
00109 }
00110 }
00111 m->even = me;
00112 m->odd = mo;
00113 m->noise = mn;
00114 m->temp = mt;
00115 }
00116
00117 static void diff_fields(struct metrics *metr, mp_image_t *old, mp_image_t *new)
00118 {
00119 struct metrics m, mu, mv;
00120 diff_planes(&m, old->planes[0], new->planes[0],
00121 new->w, new->h, old->stride[0], new->stride[0]);
00122 if (new->flags & MP_IMGFLAG_PLANAR) {
00123 diff_planes(&mu, old->planes[1], new->planes[1],
00124 new->chroma_width, new->chroma_height,
00125 old->stride[1], new->stride[1]);
00126 diff_planes(&mv, old->planes[2], new->planes[2],
00127 new->chroma_width, new->chroma_height,
00128 old->stride[2], new->stride[2]);
00129 if (mu.even > m.even) m.even = mu.even;
00130 if (mu.odd > m.odd) m.odd = mu.odd;
00131 if (mu.noise > m.noise) m.noise = mu.noise;
00132 if (mu.temp > m.temp) m.temp = mu.temp;
00133 if (mv.even > m.even) m.even = mv.even;
00134 if (mv.odd > m.odd) m.odd = mv.odd;
00135 if (mv.noise > m.noise) m.noise = mv.noise;
00136 if (mv.temp > m.temp) m.temp = mv.temp;
00137 }
00138 *metr = m;
00139 }
00140
00141 static void status(int f, struct metrics *m)
00142 {
00143 mp_msg(MSGT_VFILTER, MSGL_V, "frame %d: e=%d o=%d n=%d t=%d\n",
00144 f, m->even, m->odd, m->noise, m->temp);
00145 }
00146
00147 static int analyze_fixed_pattern(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old)
00148 {
00149 if (p->frame >= 0) p->frame = (p->frame+1)%5;
00150 mp_msg(MSGT_VFILTER, MSGL_V, "frame %d\n", p->frame);
00151 switch (p->frame) {
00152 case -1: case 0: case 1: case 2:
00153 return TC_PROG;
00154 case 3:
00155 return TC_IL1;
00156 case 4:
00157 return TC_IL2;
00158 }
00159 return 0;
00160 }
00161
00162 static int analyze_aggressive(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old)
00163 {
00164 struct metrics m, pm;
00165
00166 if (p->frame >= 0) p->frame = (p->frame+1)%5;
00167
00168 diff_fields(&m, old, new);
00169
00170 status(p->frame, &m);
00171
00172 pm = p->pm;
00173 p->pm = m;
00174
00175 if (p->frame == 4) {
00176
00177 if ((m.even > p->thres[2]) && (m.odd > p->thres[2]) && (m.temp > p->thres[3])
00178 && (m.temp > 5*pm.temp) && (m.temp*2 > m.noise)) {
00179 mp_msg(MSGT_VFILTER, MSGL_V, "scene change breaking telecine!\n");
00180 p->frame = -1;
00181 return TC_DROP;
00182 }
00183
00184 if (m.noise - m.temp > -p->thres[4]) {
00185 if (COMPARABLE(m.even, pm.odd)) {
00186
00187 return TC_IL2;
00188 } else if ((m.even < p->thres[0]) && (m.odd < p->thres[0]) && VERYCLOSE(m.even, m.odd)
00189 && VERYCLOSE(m.noise,m.temp) && VERYCLOSE(m.noise,pm.noise)) {
00190 mp_msg(MSGT_VFILTER, MSGL_V, "interlaced frame appears in duplicate!!!\n");
00191 p->pm = pm;
00192 p->frame = 3;
00193 return TC_IL1;
00194 }
00195 } else {
00196 mp_msg(MSGT_VFILTER, MSGL_V, "mismatched telecine fields!\n");
00197 p->frame = -1;
00198 }
00199 }
00200
00201 if (2*m.even*m.temp < m.odd*m.noise) {
00202 mp_msg(MSGT_VFILTER, MSGL_V, "caught telecine sync!\n");
00203 p->frame = 3;
00204 return TC_IL1;
00205 }
00206
00207 if (p->frame < 3) {
00208 if (m.noise > p->thres[3]) {
00209 if (m.noise > 2*m.temp) {
00210 mp_msg(MSGT_VFILTER, MSGL_V, "merging fields out of sequence!\n");
00211 return TC_IL2;
00212 }
00213 if ((m.noise > 2*pm.noise) && (m.even > p->thres[2]) && (m.odd > p->thres[2])) {
00214 mp_msg(MSGT_VFILTER, MSGL_V, "dropping horrible interlaced frame!\n");
00215 return TC_DROP;
00216 }
00217 }
00218 }
00219
00220 switch (p->frame) {
00221 case -1:
00222 if (4*m.noise > 5*m.temp) {
00223 mp_msg(MSGT_VFILTER, MSGL_V, "merging fields out of sequence!\n");
00224 return TC_IL2;
00225 }
00226 case 0:
00227 case 1:
00228 case 2:
00229 return TC_PROG;
00230 case 3:
00231 if ((m.even > p->thres[1]) && (m.even > m.odd) && (m.temp > m.noise)) {
00232 mp_msg(MSGT_VFILTER, MSGL_V, "lost telecine tracking!\n");
00233 p->frame = -1;
00234 return TC_PROG;
00235 }
00236 return TC_IL1;
00237 case 4:
00238 return TC_IL2;
00239 }
00240 return 0;
00241 }
00242
00243 static void copy_image(mp_image_t *dmpi, mp_image_t *mpi, int field)
00244 {
00245 switch (field) {
00246 case 0:
00247 my_memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h/2,
00248 dmpi->stride[0]*2, mpi->stride[0]*2);
00249 if (mpi->flags & MP_IMGFLAG_PLANAR) {
00250 my_memcpy_pic(dmpi->planes[1], mpi->planes[1],
00251 mpi->chroma_width, mpi->chroma_height/2,
00252 dmpi->stride[1]*2, mpi->stride[1]*2);
00253 my_memcpy_pic(dmpi->planes[2], mpi->planes[2],
00254 mpi->chroma_width, mpi->chroma_height/2,
00255 dmpi->stride[2]*2, mpi->stride[2]*2);
00256 }
00257 break;
00258 case 1:
00259 my_memcpy_pic(dmpi->planes[0]+dmpi->stride[0],
00260 mpi->planes[0]+mpi->stride[0], mpi->w, mpi->h/2,
00261 dmpi->stride[0]*2, mpi->stride[0]*2);
00262 if (mpi->flags & MP_IMGFLAG_PLANAR) {
00263 my_memcpy_pic(dmpi->planes[1]+dmpi->stride[1],
00264 mpi->planes[1]+mpi->stride[1],
00265 mpi->chroma_width, mpi->chroma_height/2,
00266 dmpi->stride[1]*2, mpi->stride[1]*2);
00267 my_memcpy_pic(dmpi->planes[2]+dmpi->stride[2],
00268 mpi->planes[2]+mpi->stride[2],
00269 mpi->chroma_width, mpi->chroma_height/2,
00270 dmpi->stride[2]*2, mpi->stride[2]*2);
00271 }
00272 break;
00273 case 2:
00274 memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h,
00275 dmpi->stride[0], mpi->stride[0]);
00276 if (mpi->flags & MP_IMGFLAG_PLANAR) {
00277 memcpy_pic(dmpi->planes[1], mpi->planes[1],
00278 mpi->chroma_width, mpi->chroma_height,
00279 dmpi->stride[1], mpi->stride[1]);
00280 memcpy_pic(dmpi->planes[2], mpi->planes[2],
00281 mpi->chroma_width, mpi->chroma_height,
00282 dmpi->stride[2], mpi->stride[2]);
00283 }
00284 break;
00285 }
00286 }
00287
00288 static int do_put_image(struct vf_instance *vf, mp_image_t *dmpi)
00289 {
00290 struct vf_priv_s *p = vf->priv;
00291 int dropflag;
00292
00293 switch (p->drop) {
00294 default:
00295 dropflag = 0;
00296 break;
00297 case 1:
00298 dropflag = (++p->lastdrop >= 5);
00299 break;
00300 case 2:
00301 dropflag = (++p->lastdrop >= 5) && (4*p->inframes <= 5*p->outframes);
00302 break;
00303 }
00304
00305 if (dropflag) {
00306 mp_msg(MSGT_VFILTER, MSGL_V, "drop! [%d/%d=%g]\n",
00307 p->outframes, p->inframes, (float)p->outframes/p->inframes);
00308 p->lastdrop = 0;
00309 return 0;
00310 }
00311
00312 p->outframes++;
00313 return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE);
00314 }
00315
00316 static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
00317 {
00318 int ret=0;
00319 mp_image_t *dmpi;
00320 struct vf_priv_s *p = vf->priv;
00321
00322 p->inframes++;
00323
00324 if (p->needread) dmpi = vf_get_image(vf->next, mpi->imgfmt,
00325 MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE |
00326 MP_IMGFLAG_PRESERVE | MP_IMGFLAG_READABLE,
00327 mpi->width, mpi->height);
00328
00329 else dmpi = vf_get_image(vf->next, mpi->imgfmt,
00330 MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE |
00331 MP_IMGFLAG_PRESERVE, mpi->width, mpi->height);
00332
00333 switch (p->analyze(p, mpi, dmpi)) {
00334 case TC_DROP:
00335
00336 if (p->needread) copy_image(dmpi, mpi, 2);
00337 p->lastdrop = 0;
00338 break;
00339 case TC_PROG:
00340
00341 copy_image(dmpi, mpi, 2);
00342 ret = do_put_image(vf, dmpi);
00343 break;
00344 case TC_IL1:
00345
00346 if (p->needread) copy_image(dmpi, mpi, 2);
00347 else copy_image(dmpi, mpi, 1);
00348 p->lastdrop = 0;
00349 break;
00350 case TC_IL2:
00351
00352 copy_image(dmpi, mpi, 0);
00353 ret = do_put_image(vf, dmpi);
00354 if (p->needread) copy_image(dmpi, mpi, 1);
00355 break;
00356 }
00357 return ret;
00358 }
00359
00360 static int query_format(struct vf_instance *vf, unsigned int fmt)
00361 {
00362
00363 switch (fmt) {
00364 case IMGFMT_YV12:
00365 case IMGFMT_IYUV:
00366 case IMGFMT_I420:
00367 return vf_next_query_format(vf, fmt);
00368 }
00369 return 0;
00370 }
00371
00372 static int config(struct vf_instance *vf,
00373 int width, int height, int d_width, int d_height,
00374 unsigned int flags, unsigned int outfmt)
00375 {
00376 return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
00377 }
00378
00379 static void uninit(struct vf_instance *vf)
00380 {
00381 free(vf->priv);
00382 }
00383
00384 static struct {
00385 const char *name;
00386 int (*func)(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old);
00387 int needread;
00388 } anal_funcs[] = {
00389 { "fixed", analyze_fixed_pattern, 0 },
00390 { "aggressive", analyze_aggressive, 1 },
00391 { NULL, NULL, 0 }
00392 };
00393
00394 #define STARTVARS if (0)
00395 #define GETVAR(str, name, out, func) \
00396 else if (!strncmp((str), name "=", sizeof(name))) \
00397 (out) = (func)((str) + sizeof(name))
00398
00399 static void parse_var(struct vf_priv_s *p, char *var)
00400 {
00401 STARTVARS;
00402 GETVAR(var, "dr", p->drop, atoi);
00403 GETVAR(var, "t0", p->thres[0], atoi);
00404 GETVAR(var, "t1", p->thres[1], atoi);
00405 GETVAR(var, "t2", p->thres[2], atoi);
00406 GETVAR(var, "t3", p->thres[3], atoi);
00407 GETVAR(var, "t4", p->thres[4], atoi);
00408 GETVAR(var, "fr", p->frame, atoi);
00409 GETVAR(var, "am", p->mode, atoi);
00410 }
00411
00412 static void parse_args(struct vf_priv_s *p, char *args)
00413 {
00414 char *next, *orig;
00415 for (args=orig=av_strdup(args); args; args=next) {
00416 next = strchr(args, ':');
00417 if (next) *next++ = 0;
00418 parse_var(p, args);
00419 }
00420 free(orig);
00421 }
00422
00423 static int vf_open(vf_instance_t *vf, char *args)
00424 {
00425 struct vf_priv_s *p;
00426 vf->config = config;
00427 vf->put_image = put_image;
00428 vf->query_format = query_format;
00429 vf->uninit = uninit;
00430 vf->default_reqs = VFCAP_ACCEPT_STRIDE;
00431 vf->priv = p = calloc(1, sizeof(struct vf_priv_s));
00432 p->frame = -1;
00433 p->thres[0] = 440;
00434 p->thres[1] = 720;
00435 p->thres[2] = 2500;
00436 p->thres[3] = 2500;
00437 p->thres[4] = 800;
00438 p->drop = 0;
00439 p->mode = 1;
00440 if (args) parse_args(p, args);
00441 p->analyze = anal_funcs[p->mode].func;
00442 p->needread = anal_funcs[p->mode].needread;
00443 return 1;
00444 }
00445
00446 const vf_info_t vf_info_detc = {
00447 "de-telecine filter",
00448 "detc",
00449 "Rich Felker",
00450 "",
00451 vf_open,
00452 NULL
00453 };