diff options
Diffstat (limited to 'libavfilter/libmpcodecs/vf_detc.c')
-rw-r--r-- | libavfilter/libmpcodecs/vf_detc.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/libavfilter/libmpcodecs/vf_detc.c b/libavfilter/libmpcodecs/vf_detc.c new file mode 100644 index 0000000..28d20e0 --- /dev/null +++ b/libavfilter/libmpcodecs/vf_detc.c @@ -0,0 +1,453 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "mp_msg.h" + +#include "img_format.h" +#include "mp_image.h" +#include "vf.h" + +#include "libvo/fastmemcpy.h" + +struct metrics { + int even; + int odd; + int noise; + int temp; +}; + +struct vf_priv_s { + int frame; + int drop, lastdrop; + struct metrics pm; + int thres[5]; + int inframes, outframes; + int mode; + int (*analyze)(struct vf_priv_s *, mp_image_t *, mp_image_t *); + int needread; +}; + +#define COMPE(a,b,e) (abs((a)-(b)) < (((a)+(b))>>(e))) +#define COMPARABLE(a,b) COMPE((a),(b),2) +#define VERYCLOSE(a,b) COMPE((a),(b),3) + +#define OUTER_TC_NBHD(s) ( \ + COMPARABLE((s)[-1].m.even,(s)[-1].m.odd) && \ + COMPARABLE((s)[1].m.even,(s)[0].m.odd) && \ + COMPARABLE((s)[2].m.even,(s)[1].m.odd) && \ + COMPARABLE((s)[-1].m.noise,(s)[0].m.temp) && \ + COMPARABLE((s)[2].m.noise,(s)[2].m.temp) ) + +#define INNER_TC_NBHD(s,l,h) ( \ + COMPARABLE((s)[0].m.even,(l)) && \ + COMPARABLE((s)[2].m.odd,(l)) && ( \ + COMPARABLE((s)[0].m.noise,(h)) || \ + COMPARABLE((s)[1].m.noise,(h)) ) ) + +enum { + TC_DROP, + TC_PROG, + TC_IL1, + TC_IL2 +}; + +static void block_diffs(struct metrics *m, unsigned char *old, unsigned char *new, int os, int ns) +{ + int x, y, even=0, odd=0, noise, temp; + unsigned char *oldp, *newp; + m->noise = m->temp = 0; + for (x = 8; x; x--) { + oldp = old++; + newp = new++; + noise = temp = 0; + for (y = 4; y; y--) { + even += abs(newp[0]-oldp[0]); + odd += abs(newp[ns]-oldp[os]); + noise += newp[ns]-newp[0]; + temp += oldp[os]-newp[0]; + oldp += os<<1; + newp += ns<<1; + } + m->noise += abs(noise); + m->temp += abs(temp); + } + m->even = even; + m->odd = odd; +} + +static void diff_planes(struct metrics *m, unsigned char *old, unsigned char *new, int w, int h, int os, int ns) +{ + int x, y, me=0, mo=0, mn=0, mt=0; + struct metrics l; + for (y = 0; y < h-7; y += 8) { + for (x = 0; x < w-7; x += 8) { + block_diffs(&l, old+x+y*os, new+x+y*ns, os, ns); + if (l.even > me) me = l.even; + if (l.odd > mo) mo = l.odd; + if (l.noise > mn) mn = l.noise; + if (l.temp > mt) mt = l.temp; + } + } + m->even = me; + m->odd = mo; + m->noise = mn; + m->temp = mt; +} + +static void diff_fields(struct metrics *metr, mp_image_t *old, mp_image_t *new) +{ + struct metrics m, mu, mv; + diff_planes(&m, old->planes[0], new->planes[0], + new->w, new->h, old->stride[0], new->stride[0]); + if (new->flags & MP_IMGFLAG_PLANAR) { + diff_planes(&mu, old->planes[1], new->planes[1], + new->chroma_width, new->chroma_height, + old->stride[1], new->stride[1]); + diff_planes(&mv, old->planes[2], new->planes[2], + new->chroma_width, new->chroma_height, + old->stride[2], new->stride[2]); + if (mu.even > m.even) m.even = mu.even; + if (mu.odd > m.odd) m.odd = mu.odd; + if (mu.noise > m.noise) m.noise = mu.noise; + if (mu.temp > m.temp) m.temp = mu.temp; + if (mv.even > m.even) m.even = mv.even; + if (mv.odd > m.odd) m.odd = mv.odd; + if (mv.noise > m.noise) m.noise = mv.noise; + if (mv.temp > m.temp) m.temp = mv.temp; + } + *metr = m; +} + +static void status(int f, struct metrics *m) +{ + mp_msg(MSGT_VFILTER, MSGL_V, "frame %d: e=%d o=%d n=%d t=%d\n", + f, m->even, m->odd, m->noise, m->temp); +} + +static int analyze_fixed_pattern(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old) +{ + if (p->frame >= 0) p->frame = (p->frame+1)%5; + mp_msg(MSGT_VFILTER, MSGL_V, "frame %d\n", p->frame); + switch (p->frame) { + case -1: case 0: case 1: case 2: + return TC_PROG; + case 3: + return TC_IL1; + case 4: + return TC_IL2; + } + return 0; +} + +static int analyze_aggressive(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old) +{ + struct metrics m, pm; + + if (p->frame >= 0) p->frame = (p->frame+1)%5; + + diff_fields(&m, old, new); + + status(p->frame, &m); + + pm = p->pm; + p->pm = m; + + if (p->frame == 4) { + /* We need to break at scene changes, but is this a valid test? */ + if ((m.even > p->thres[2]) && (m.odd > p->thres[2]) && (m.temp > p->thres[3]) + && (m.temp > 5*pm.temp) && (m.temp*2 > m.noise)) { + mp_msg(MSGT_VFILTER, MSGL_V, "scene change breaking telecine!\n"); + p->frame = -1; + return TC_DROP; + } + /* Thres. is to compensate for quantization errors when noise is low */ + if (m.noise - m.temp > -p->thres[4]) { + if (COMPARABLE(m.even, pm.odd)) { + //mp_msg(MSGT_VFILTER, MSGL_V, "confirmed field match!\n"); + return TC_IL2; + } else if ((m.even < p->thres[0]) && (m.odd < p->thres[0]) && VERYCLOSE(m.even, m.odd) + && VERYCLOSE(m.noise,m.temp) && VERYCLOSE(m.noise,pm.noise)) { + mp_msg(MSGT_VFILTER, MSGL_V, "interlaced frame appears in duplicate!!!\n"); + p->pm = pm; /* hack :) */ + p->frame = 3; + return TC_IL1; + } + } else { + mp_msg(MSGT_VFILTER, MSGL_V, "mismatched telecine fields!\n"); + p->frame = -1; + } + } + + if (2*m.even*m.temp < m.odd*m.noise) { + mp_msg(MSGT_VFILTER, MSGL_V, "caught telecine sync!\n"); + p->frame = 3; + return TC_IL1; + } + + if (p->frame < 3) { + if (m.noise > p->thres[3]) { + if (m.noise > 2*m.temp) { + mp_msg(MSGT_VFILTER, MSGL_V, "merging fields out of sequence!\n"); + return TC_IL2; + } + if ((m.noise > 2*pm.noise) && (m.even > p->thres[2]) && (m.odd > p->thres[2])) { + mp_msg(MSGT_VFILTER, MSGL_V, "dropping horrible interlaced frame!\n"); + return TC_DROP; + } + } + } + + switch (p->frame) { + case -1: + if (4*m.noise > 5*m.temp) { + mp_msg(MSGT_VFILTER, MSGL_V, "merging fields out of sequence!\n"); + return TC_IL2; + } + case 0: + case 1: + case 2: + return TC_PROG; + case 3: + if ((m.even > p->thres[1]) && (m.even > m.odd) && (m.temp > m.noise)) { + mp_msg(MSGT_VFILTER, MSGL_V, "lost telecine tracking!\n"); + p->frame = -1; + return TC_PROG; + } + return TC_IL1; + case 4: + return TC_IL2; + } + return 0; +} + +static void copy_image(mp_image_t *dmpi, mp_image_t *mpi, int field) +{ + switch (field) { + case 0: + my_memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h/2, + dmpi->stride[0]*2, mpi->stride[0]*2); + if (mpi->flags & MP_IMGFLAG_PLANAR) { + my_memcpy_pic(dmpi->planes[1], mpi->planes[1], + mpi->chroma_width, mpi->chroma_height/2, + dmpi->stride[1]*2, mpi->stride[1]*2); + my_memcpy_pic(dmpi->planes[2], mpi->planes[2], + mpi->chroma_width, mpi->chroma_height/2, + dmpi->stride[2]*2, mpi->stride[2]*2); + } + break; + case 1: + my_memcpy_pic(dmpi->planes[0]+dmpi->stride[0], + mpi->planes[0]+mpi->stride[0], mpi->w, mpi->h/2, + dmpi->stride[0]*2, mpi->stride[0]*2); + if (mpi->flags & MP_IMGFLAG_PLANAR) { + my_memcpy_pic(dmpi->planes[1]+dmpi->stride[1], + mpi->planes[1]+mpi->stride[1], + mpi->chroma_width, mpi->chroma_height/2, + dmpi->stride[1]*2, mpi->stride[1]*2); + my_memcpy_pic(dmpi->planes[2]+dmpi->stride[2], + mpi->planes[2]+mpi->stride[2], + mpi->chroma_width, mpi->chroma_height/2, + dmpi->stride[2]*2, mpi->stride[2]*2); + } + break; + case 2: + memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h, + dmpi->stride[0], mpi->stride[0]); + if (mpi->flags & MP_IMGFLAG_PLANAR) { + memcpy_pic(dmpi->planes[1], mpi->planes[1], + mpi->chroma_width, mpi->chroma_height, + dmpi->stride[1], mpi->stride[1]); + memcpy_pic(dmpi->planes[2], mpi->planes[2], + mpi->chroma_width, mpi->chroma_height, + dmpi->stride[2], mpi->stride[2]); + } + break; + } +} + +static int do_put_image(struct vf_instance *vf, mp_image_t *dmpi) +{ + struct vf_priv_s *p = vf->priv; + int dropflag; + + switch (p->drop) { + default: + dropflag = 0; + break; + case 1: + dropflag = (++p->lastdrop >= 5); + break; + case 2: + dropflag = (++p->lastdrop >= 5) && (4*p->inframes <= 5*p->outframes); + break; + } + + if (dropflag) { + mp_msg(MSGT_VFILTER, MSGL_V, "drop! [%d/%d=%g]\n", + p->outframes, p->inframes, (float)p->outframes/p->inframes); + p->lastdrop = 0; + return 0; + } + + p->outframes++; + return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); +} + +static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts) +{ + int ret=0; + mp_image_t *dmpi; + struct vf_priv_s *p = vf->priv; + + p->inframes++; + + if (p->needread) dmpi = vf_get_image(vf->next, mpi->imgfmt, + MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE | + MP_IMGFLAG_PRESERVE | MP_IMGFLAG_READABLE, + mpi->width, mpi->height); + /* FIXME: is there a good way to get rid of static type? */ + else dmpi = vf_get_image(vf->next, mpi->imgfmt, + MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE | + MP_IMGFLAG_PRESERVE, mpi->width, mpi->height); + + switch (p->analyze(p, mpi, dmpi)) { + case TC_DROP: + /* Don't copy anything unless we'll need to read it. */ + if (p->needread) copy_image(dmpi, mpi, 2); + p->lastdrop = 0; + break; + case TC_PROG: + /* Copy and display the whole frame. */ + copy_image(dmpi, mpi, 2); + ret = do_put_image(vf, dmpi); + break; + case TC_IL1: + /* Only copy bottom field unless we need to read. */ + if (p->needread) copy_image(dmpi, mpi, 2); + else copy_image(dmpi, mpi, 1); + p->lastdrop = 0; + break; + case TC_IL2: + /* Copy top field and show frame, then copy bottom if needed. */ + copy_image(dmpi, mpi, 0); + ret = do_put_image(vf, dmpi); + if (p->needread) copy_image(dmpi, mpi, 1); + break; + } + return ret; +} + +static int query_format(struct vf_instance *vf, unsigned int fmt) +{ + /* FIXME - figure out which other formats work */ + switch (fmt) { + case IMGFMT_YV12: + case IMGFMT_IYUV: + case IMGFMT_I420: + return vf_next_query_format(vf, fmt); + } + return 0; +} + +static int config(struct vf_instance *vf, + int width, int height, int d_width, int d_height, + unsigned int flags, unsigned int outfmt) +{ + return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt); +} + +static void uninit(struct vf_instance *vf) +{ + free(vf->priv); +} + +static struct { + const char *name; + int (*func)(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old); + int needread; +} anal_funcs[] = { + { "fixed", analyze_fixed_pattern, 0 }, + { "aggressive", analyze_aggressive, 1 }, + { NULL, NULL, 0 } +}; + +#define STARTVARS if (0) +#define GETVAR(str, name, out, func) \ + else if (!strncmp((str), name "=", sizeof(name))) \ + (out) = (func)((str) + sizeof(name)) + +static void parse_var(struct vf_priv_s *p, char *var) +{ + STARTVARS; + GETVAR(var, "dr", p->drop, atoi); + GETVAR(var, "t0", p->thres[0], atoi); + GETVAR(var, "t1", p->thres[1], atoi); + GETVAR(var, "t2", p->thres[2], atoi); + GETVAR(var, "t3", p->thres[3], atoi); + GETVAR(var, "t4", p->thres[4], atoi); + GETVAR(var, "fr", p->frame, atoi); + GETVAR(var, "am", p->mode, atoi); +} + +static void parse_args(struct vf_priv_s *p, char *args) +{ + char *next, *orig; + for (args=orig=av_strdup(args); args; args=next) { + next = strchr(args, ':'); + if (next) *next++ = 0; + parse_var(p, args); + } + free(orig); +} + +static int vf_open(vf_instance_t *vf, char *args) +{ + struct vf_priv_s *p; + vf->config = config; + vf->put_image = put_image; + vf->query_format = query_format; + vf->uninit = uninit; + vf->default_reqs = VFCAP_ACCEPT_STRIDE; + vf->priv = p = calloc(1, sizeof(struct vf_priv_s)); + p->frame = -1; + p->thres[0] = 440; + p->thres[1] = 720; + p->thres[2] = 2500; + p->thres[3] = 2500; + p->thres[4] = 800; + p->drop = 0; + p->mode = 1; + if (args) parse_args(p, args); + p->analyze = anal_funcs[p->mode].func; + p->needread = anal_funcs[p->mode].needread; + return 1; +} + +const vf_info_t vf_info_detc = { + "de-telecine filter", + "detc", + "Rich Felker", + "", + vf_open, + NULL +}; |