typo fixes
[mplayer/greg.git] / libmpcodecs / vf_detc.c
blob40d68f880e1208cd7aaf7dec8b3625fd2d9e31a7
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
5 #include "config.h"
6 #include "mp_msg.h"
8 #include "img_format.h"
9 #include "mp_image.h"
10 #include "vf.h"
12 #include "libvo/fastmemcpy.h"
14 struct metrics {
15 int even;
16 int odd;
17 int noise;
18 int temp;
21 struct vf_priv_s {
22 int frame;
23 int drop, lastdrop;
24 struct metrics pm;
25 int thres[5];
26 int inframes, outframes;
27 int mode;
28 int (*analyze)(struct vf_priv_s *, mp_image_t *, mp_image_t *);
29 int needread;
32 #define COMPE(a,b,e) (abs((a)-(b)) < (((a)+(b))>>(e)))
33 #define COMPARABLE(a,b) COMPE((a),(b),2)
34 #define VERYCLOSE(a,b) COMPE((a),(b),3)
36 #define OUTER_TC_NBHD(s) ( \
37 COMPARABLE((s)[-1].m.even,(s)[-1].m.odd) && \
38 COMPARABLE((s)[1].m.even,(s)[0].m.odd) && \
39 COMPARABLE((s)[2].m.even,(s)[1].m.odd) && \
40 COMPARABLE((s)[-1].m.noise,(s)[0].m.temp) && \
41 COMPARABLE((s)[2].m.noise,(s)[2].m.temp) )
43 #define INNER_TC_NBHD(s,l,h) ( \
44 COMPARABLE((s)[0].m.even,(l)) && \
45 COMPARABLE((s)[2].m.odd,(l)) && ( \
46 COMPARABLE((s)[0].m.noise,(h)) || \
47 COMPARABLE((s)[1].m.noise,(h)) ) )
49 enum {
50 TC_DROP,
51 TC_PROG,
52 TC_IL1,
53 TC_IL2
56 static inline void *my_memcpy_pic(void * dst, void * src, int bytesPerLine, int height, int dstStride, int srcStride)
58 int i;
59 void *retval=dst;
61 for(i=0; i<height; i++)
63 memcpy(dst, src, bytesPerLine);
64 src+= srcStride;
65 dst+= dstStride;
68 return retval;
71 static unsigned int hash_pic(unsigned char *img, int w, int h, int stride)
73 int step = w*h/1024;
74 unsigned int hash=0;
75 int x=0, y;
77 step -= step % 3;
79 for (y=0; y<h; y++) {
80 for (; x<w; x+=step) {
81 hash = hash ^ (hash<<4) ^ img[x];
83 x -= w;
84 img += stride;
87 return hash;
90 static void block_diffs(struct metrics *m, unsigned char *old, unsigned char *new, int os, int ns)
92 int x, y, even=0, odd=0, noise, temp;
93 unsigned char *oldp, *newp;
94 m->noise = m->temp = 0;
95 for (x = 8; x; x--) {
96 oldp = old++;
97 newp = new++;
98 noise = temp = 0;
99 for (y = 4; y; y--) {
100 even += abs(newp[0]-oldp[0]);
101 odd += abs(newp[ns]-oldp[os]);
102 noise += newp[ns]-newp[0];
103 temp += oldp[os]-newp[0];
104 oldp += os<<1;
105 newp += ns<<1;
107 m->noise += abs(noise);
108 m->temp += abs(temp);
110 m->even = even;
111 m->odd = odd;
114 static void diff_planes(struct metrics *m, unsigned char *old, unsigned char *new, int w, int h, int os, int ns)
116 int x, y, me=0, mo=0, mn=0, mt=0;
117 struct metrics l;
118 for (y = 0; y < h-7; y += 8) {
119 for (x = 0; x < w-7; x += 8) {
120 block_diffs(&l, old+x+y*os, new+x+y*ns, os, ns);
121 if (l.even > me) me = l.even;
122 if (l.odd > mo) mo = l.odd;
123 if (l.noise > mn) mn = l.noise;
124 if (l.temp > mt) mt = l.temp;
127 m->even = me;
128 m->odd = mo;
129 m->noise = mn;
130 m->temp = mt;
133 static void diff_fields(struct metrics *metr, mp_image_t *old, mp_image_t *new)
135 struct metrics m, mu, mv;
136 diff_planes(&m, old->planes[0], new->planes[0],
137 new->w, new->h, old->stride[0], new->stride[0]);
138 if (new->flags & MP_IMGFLAG_PLANAR) {
139 diff_planes(&mu, old->planes[1], new->planes[1],
140 new->chroma_width, new->chroma_height,
141 old->stride[1], new->stride[1]);
142 diff_planes(&mv, old->planes[2], new->planes[2],
143 new->chroma_width, new->chroma_height,
144 old->stride[2], new->stride[2]);
145 if (mu.even > m.even) m.even = mu.even;
146 if (mu.odd > m.odd) m.odd = mu.odd;
147 if (mu.noise > m.noise) m.noise = mu.noise;
148 if (mu.temp > m.temp) m.temp = mu.temp;
149 if (mv.even > m.even) m.even = mv.even;
150 if (mv.odd > m.odd) m.odd = mv.odd;
151 if (mv.noise > m.noise) m.noise = mv.noise;
152 if (mv.temp > m.temp) m.temp = mv.temp;
154 *metr = m;
157 static void status(int f, struct metrics *m)
159 mp_msg(MSGT_VFILTER, MSGL_V, "frame %d: e=%d o=%d n=%d t=%d\n",
160 f, m->even, m->odd, m->noise, m->temp);
163 static int analyze_fixed_pattern(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old)
165 if (p->frame >= 0) p->frame = (p->frame+1)%5;
166 mp_msg(MSGT_VFILTER, MSGL_V, "frame %d\n", p->frame);
167 switch (p->frame) {
168 case -1: case 0: case 1: case 2:
169 return TC_PROG;
170 case 3:
171 return TC_IL1;
172 case 4:
173 return TC_IL2;
175 return 0;
178 static int analyze_aggressive(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old)
180 int i;
181 struct metrics m, pm;
183 if (p->frame >= 0) p->frame = (p->frame+1)%5;
185 diff_fields(&m, old, new);
187 status(p->frame, &m);
189 pm = p->pm;
190 p->pm = m;
192 if (p->frame == 4) {
193 /* We need to break at scene changes, but is this a valid test? */
194 if ((m.even > p->thres[2]) && (m.odd > p->thres[2]) && (m.temp > p->thres[3])
195 && (m.temp > 5*pm.temp) && (m.temp*2 > m.noise)) {
196 mp_msg(MSGT_VFILTER, MSGL_V, "scene change breaking telecine!\n");
197 p->frame = -1;
198 return TC_DROP;
200 /* Thres. is to compensate for quantization errors when noise is low */
201 if (m.noise - m.temp > -p->thres[4]) {
202 if (COMPARABLE(m.even, pm.odd)) {
203 //mp_msg(MSGT_VFILTER, MSGL_V, "confirmed field match!\n");
204 return TC_IL2;
205 } else if ((m.even < p->thres[0]) && (m.odd < p->thres[0]) && VERYCLOSE(m.even, m.odd)
206 && VERYCLOSE(m.noise,m.temp) && VERYCLOSE(m.noise,pm.noise)) {
207 mp_msg(MSGT_VFILTER, MSGL_V, "interlaced frame appears in duplicate!!!\n");
208 p->pm = pm; /* hack :) */
209 p->frame = 3;
210 return TC_IL1;
212 } else {
213 mp_msg(MSGT_VFILTER, MSGL_V, "mismatched telecine fields!\n");
214 p->frame = -1;
218 if (2*m.even*m.temp < m.odd*m.noise) {
219 mp_msg(MSGT_VFILTER, MSGL_V, "caught telecine sync!\n");
220 p->frame = 3;
221 return TC_IL1;
224 if (p->frame < 3) {
225 if (m.noise > p->thres[3]) {
226 if (m.noise > 2*m.temp) {
227 mp_msg(MSGT_VFILTER, MSGL_V, "merging fields out of sequence!\n");
228 return TC_IL2;
230 if ((m.noise > 2*pm.noise) && (m.even > p->thres[2]) && (m.odd > p->thres[2])) {
231 mp_msg(MSGT_VFILTER, MSGL_V, "dropping horrible interlaced frame!\n");
232 return TC_DROP;
237 switch (p->frame) {
238 case -1:
239 if (4*m.noise > 5*m.temp) {
240 mp_msg(MSGT_VFILTER, MSGL_V, "merging fields out of sequence!\n");
241 return TC_IL2;
243 case 0:
244 case 1:
245 case 2:
246 return TC_PROG;
247 case 3:
248 if ((m.even > p->thres[1]) && (m.even > m.odd) && (m.temp > m.noise)) {
249 mp_msg(MSGT_VFILTER, MSGL_V, "lost telecine tracking!\n");
250 p->frame = -1;
251 return TC_PROG;
253 return TC_IL1;
254 case 4:
255 return TC_IL2;
257 return 0;
260 static void copy_image(mp_image_t *dmpi, mp_image_t *mpi, int field)
262 switch (field) {
263 case 0:
264 my_memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h/2,
265 dmpi->stride[0]*2, mpi->stride[0]*2);
266 if (mpi->flags & MP_IMGFLAG_PLANAR) {
267 my_memcpy_pic(dmpi->planes[1], mpi->planes[1],
268 mpi->chroma_width, mpi->chroma_height/2,
269 dmpi->stride[1]*2, mpi->stride[1]*2);
270 my_memcpy_pic(dmpi->planes[2], mpi->planes[2],
271 mpi->chroma_width, mpi->chroma_height/2,
272 dmpi->stride[2]*2, mpi->stride[2]*2);
274 break;
275 case 1:
276 my_memcpy_pic(dmpi->planes[0]+dmpi->stride[0],
277 mpi->planes[0]+mpi->stride[0], mpi->w, mpi->h/2,
278 dmpi->stride[0]*2, mpi->stride[0]*2);
279 if (mpi->flags & MP_IMGFLAG_PLANAR) {
280 my_memcpy_pic(dmpi->planes[1]+dmpi->stride[1],
281 mpi->planes[1]+mpi->stride[1],
282 mpi->chroma_width, mpi->chroma_height/2,
283 dmpi->stride[1]*2, mpi->stride[1]*2);
284 my_memcpy_pic(dmpi->planes[2]+dmpi->stride[2],
285 mpi->planes[2]+mpi->stride[2],
286 mpi->chroma_width, mpi->chroma_height/2,
287 dmpi->stride[2]*2, mpi->stride[2]*2);
289 break;
290 case 2:
291 memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h,
292 dmpi->stride[0], mpi->stride[0]);
293 if (mpi->flags & MP_IMGFLAG_PLANAR) {
294 memcpy_pic(dmpi->planes[1], mpi->planes[1],
295 mpi->chroma_width, mpi->chroma_height,
296 dmpi->stride[1], mpi->stride[1]);
297 memcpy_pic(dmpi->planes[2], mpi->planes[2],
298 mpi->chroma_width, mpi->chroma_height,
299 dmpi->stride[2], mpi->stride[2]);
301 break;
305 static int do_put_image(struct vf_instance_s* vf, mp_image_t *dmpi)
307 struct vf_priv_s *p = vf->priv;
308 int dropflag;
310 switch (p->drop) {
311 case 0:
312 dropflag = 0;
313 break;
314 case 1:
315 dropflag = (++p->lastdrop >= 5);
316 break;
317 case 2:
318 dropflag = (++p->lastdrop >= 5) && (4*p->inframes <= 5*p->outframes);
319 break;
322 if (dropflag) {
323 mp_msg(MSGT_VFILTER, MSGL_V, "drop! [%d/%d=%g]\n",
324 p->outframes, p->inframes, (float)p->outframes/p->inframes);
325 p->lastdrop = 0;
326 return 0;
329 p->outframes++;
330 return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE);
333 static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts)
335 int ret=0;
336 mp_image_t *dmpi;
337 struct vf_priv_s *p = vf->priv;
339 p->inframes++;
341 if (p->needread) dmpi = vf_get_image(vf->next, mpi->imgfmt,
342 MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE |
343 MP_IMGFLAG_PRESERVE | MP_IMGFLAG_READABLE,
344 mpi->width, mpi->height);
345 /* FIXME: is there a good way to get rid of static type? */
346 else dmpi = vf_get_image(vf->next, mpi->imgfmt,
347 MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE |
348 MP_IMGFLAG_PRESERVE, mpi->width, mpi->height);
350 switch (p->analyze(p, mpi, dmpi)) {
351 case TC_DROP:
352 /* Don't copy anything unless we'll need to read it. */
353 if (p->needread) copy_image(dmpi, mpi, 2);
354 p->lastdrop = 0;
355 break;
356 case TC_PROG:
357 /* Copy and display the whole frame. */
358 copy_image(dmpi, mpi, 2);
359 ret = do_put_image(vf, dmpi);
360 break;
361 case TC_IL1:
362 /* Only copy bottom field unless we need to read. */
363 if (p->needread) copy_image(dmpi, mpi, 2);
364 else copy_image(dmpi, mpi, 1);
365 p->lastdrop = 0;
366 break;
367 case TC_IL2:
368 /* Copy top field and show frame, then copy bottom if needed. */
369 copy_image(dmpi, mpi, 0);
370 ret = do_put_image(vf, dmpi);
371 if (p->needread) copy_image(dmpi, mpi, 1);
372 break;
374 return ret;
377 static int query_format(struct vf_instance_s* vf, unsigned int fmt)
379 /* FIXME - figure out which other formats work */
380 switch (fmt) {
381 case IMGFMT_YV12:
382 case IMGFMT_IYUV:
383 case IMGFMT_I420:
384 return vf_next_query_format(vf, fmt);
386 return 0;
389 static int config(struct vf_instance_s* vf,
390 int width, int height, int d_width, int d_height,
391 unsigned int flags, unsigned int outfmt)
393 return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
396 static void uninit(struct vf_instance_s* vf)
398 free(vf->priv);
401 static struct {
402 char *name;
403 int (*func)(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old);
404 int needread;
405 } anal_funcs[] = {
406 { "fixed", analyze_fixed_pattern, 0 },
407 { "aggressive", analyze_aggressive, 1 },
408 { NULL, NULL, 0 }
411 #define STARTVARS if (0)
412 #define GETVAR(str, name, out, func) \
413 else if (!strncmp((str), name "=", sizeof(name))) \
414 (out) = (func)((str) + sizeof(name))
416 static void parse_var(struct vf_priv_s *p, char *var)
418 STARTVARS;
419 GETVAR(var, "dr", p->drop, atoi);
420 GETVAR(var, "t0", p->thres[0], atoi);
421 GETVAR(var, "t1", p->thres[1], atoi);
422 GETVAR(var, "t2", p->thres[2], atoi);
423 GETVAR(var, "t3", p->thres[3], atoi);
424 GETVAR(var, "t4", p->thres[4], atoi);
425 GETVAR(var, "fr", p->frame, atoi);
426 GETVAR(var, "am", p->mode, atoi);
429 static void parse_args(struct vf_priv_s *p, char *args)
431 char *next, *orig;
432 for (args=orig=strdup(args); args; args=next) {
433 next = strchr(args, ':');
434 if (next) *next++ = 0;
435 parse_var(p, args);
437 free(orig);
440 static int open(vf_instance_t *vf, char* args)
442 struct vf_priv_s *p;
443 vf->config = config;
444 vf->put_image = put_image;
445 vf->query_format = query_format;
446 vf->uninit = uninit;
447 vf->default_reqs = VFCAP_ACCEPT_STRIDE;
448 vf->priv = p = calloc(1, sizeof(struct vf_priv_s));
449 p->frame = -1;
450 p->thres[0] = 440;
451 p->thres[1] = 720;
452 p->thres[2] = 2500;
453 p->thres[3] = 2500;
454 p->thres[4] = 800;
455 p->drop = 0;
456 p->mode = 1;
457 if (args) parse_args(p, args);
458 p->analyze = anal_funcs[p->mode].func;
459 p->needread = anal_funcs[p->mode].needread;
460 return 1;
463 vf_info_t vf_info_detc = {
464 "de-telecine filter",
465 "detc",
466 "Rich Felker",
468 open,
469 NULL