~ubuntu-dev/mplayer/ubuntu-feisty

« back to all changes in this revision

Viewing changes to libmpcodecs/vf_detc.c

  • Committer: Reinhard Tartler
  • Date: 2006-07-08 08:45:33 UTC
  • Revision ID: siretart@tauware.de-20060708084533-dbc155bde7122e78
imported mplayer_0.99+1.0pre7try2+cvs20060117

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <stdio.h>
 
2
#include <stdlib.h>
 
3
#include <string.h>
 
4
 
 
5
#include "config.h"
 
6
#include "mp_msg.h"
 
7
 
 
8
#include "img_format.h"
 
9
#include "mp_image.h"
 
10
#include "vf.h"
 
11
 
 
12
#include "libvo/fastmemcpy.h"
 
13
 
 
14
struct metrics {
 
15
        int even;
 
16
        int odd;
 
17
        int noise;
 
18
        int temp;
 
19
};
 
20
 
 
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;
 
30
};
 
31
 
 
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)
 
35
 
 
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) )
 
42
 
 
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)) ) )
 
48
 
 
49
enum {
 
50
        TC_DROP,
 
51
        TC_PROG,
 
52
        TC_IL1,
 
53
        TC_IL2
 
54
};
 
55
 
 
56
static inline void *my_memcpy_pic(void * dst, void * src, int bytesPerLine, int height, int dstStride, int srcStride)
 
57
{
 
58
        int i;
 
59
        void *retval=dst;
 
60
 
 
61
        for(i=0; i<height; i++)
 
62
        {
 
63
                memcpy(dst, src, bytesPerLine);
 
64
                src+= srcStride;
 
65
                dst+= dstStride;
 
66
        }
 
67
 
 
68
        return retval;
 
69
}
 
70
 
 
71
static unsigned int hash_pic(unsigned char *img, int w, int h, int stride)
 
72
{
 
73
        int step = w*h/1024;
 
74
        unsigned int hash=0;
 
75
        int x=0, y;
 
76
 
 
77
        step -= step % 3;
 
78
 
 
79
        for (y=0; y<h; y++) {
 
80
                for (; x<w; x+=step) {
 
81
                        hash = hash ^ (hash<<4) ^ img[x];
 
82
                }
 
83
                x -= w;
 
84
                img += stride;
 
85
        }
 
86
        
 
87
        return hash;
 
88
}
 
89
 
 
90
static void block_diffs(struct metrics *m, unsigned char *old, unsigned char *new, int os, int ns)
 
91
{
 
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;
 
106
                }
 
107
                m->noise += abs(noise);
 
108
                m->temp += abs(temp);
 
109
        }
 
110
        m->even = even;
 
111
        m->odd = odd;
 
112
}
 
113
 
 
114
static void diff_planes(struct metrics *m, unsigned char *old, unsigned char *new, int w, int h, int os, int ns)
 
115
{
 
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;
 
125
                }
 
126
        }
 
127
        m->even = me;
 
128
        m->odd = mo;
 
129
        m->noise = mn;
 
130
        m->temp = mt;
 
131
}
 
132
 
 
133
static void diff_fields(struct metrics *metr, mp_image_t *old, mp_image_t *new)
 
134
{
 
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;
 
153
        }
 
154
        *metr = m;
 
155
}
 
156
 
 
157
static void status(int f, struct metrics *m)
 
158
{
 
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);
 
161
}
 
162
 
 
163
static int analyze_fixed_pattern(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old)
 
164
{
 
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;
 
174
        }
 
175
        return 0;
 
176
}
 
177
 
 
178
static int analyze_aggressive(struct vf_priv_s *p, mp_image_t *new, mp_image_t *old)
 
179
{
 
180
        int i;
 
181
        struct metrics m, pm;
 
182
        
 
183
        if (p->frame >= 0) p->frame = (p->frame+1)%5;
 
184
        
 
185
        diff_fields(&m, old, new);
 
186
        
 
187
        status(p->frame, &m);
 
188
 
 
189
        pm = p->pm;
 
190
        p->pm = m;
 
191
 
 
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;
 
199
                }
 
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;
 
211
                        } 
 
212
                } else {
 
213
                        mp_msg(MSGT_VFILTER, MSGL_V, "mismatched telecine fields!\n");
 
214
                        p->frame = -1;
 
215
                }
 
216
        }
 
217
 
 
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;
 
222
        }
 
223
 
 
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;
 
229
                        }
 
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;
 
233
                        }
 
234
                }
 
235
        }
 
236
 
 
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;
 
242
                }
 
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;
 
252
                }
 
253
                return TC_IL1;
 
254
        case 4:
 
255
                return TC_IL2;
 
256
        }
 
257
        return 0;
 
258
}
 
259
 
 
260
static void copy_image(mp_image_t *dmpi, mp_image_t *mpi, int field)
 
261
{
 
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);
 
273
                }
 
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);
 
288
                }
 
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]);
 
300
                }
 
301
                break;
 
302
        }
 
303
}
 
304
 
 
305
static int do_put_image(struct vf_instance_s* vf, mp_image_t *dmpi)
 
306
{
 
307
        struct vf_priv_s *p = vf->priv;
 
308
        int dropflag;
 
309
 
 
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;
 
320
        }
 
321
        
 
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;
 
327
        }
 
328
 
 
329
        p->outframes++;
 
330
        return vf_next_put_image(vf, dmpi);
 
331
}
 
332
 
 
333
static int put_image(struct vf_instance_s* vf, mp_image_t *mpi)
 
334
{
 
335
        int ret=0;
 
336
        mp_image_t *dmpi;
 
337
        struct vf_priv_s *p = vf->priv;
 
338
 
 
339
        p->inframes++;
 
340
 
 
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);
 
349
                
 
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;
 
373
        }
 
374
        return ret;
 
375
}
 
376
 
 
377
static int query_format(struct vf_instance_s* vf, unsigned int fmt)
 
378
{
 
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);
 
385
        }
 
386
        return 0;
 
387
}
 
388
 
 
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)
 
392
{
 
393
        return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
 
394
}
 
395
 
 
396
static void uninit(struct vf_instance_s* vf)
 
397
{
 
398
        free(vf->priv);
 
399
}
 
400
 
 
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 }
 
409
};
 
410
 
 
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))
 
415
 
 
416
static void parse_var(struct vf_priv_s *p, char *var)
 
417
{
 
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);
 
427
}
 
428
 
 
429
static void parse_args(struct vf_priv_s *p, char *args)
 
430
{
 
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);
 
436
        }
 
437
        free(orig);
 
438
}
 
439
 
 
440
static int open(vf_instance_t *vf, char* args)
 
441
{
 
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;
 
461
}
 
462
 
 
463
vf_info_t vf_info_detc = {
 
464
    "de-telecine filter",
 
465
    "detc",
 
466
    "Rich Felker",
 
467
    "",
 
468
    open,
 
469
    NULL
 
470
};
 
471
 
 
472