8 #include "img_format.h"
12 #include "libvo/fastmemcpy.h"
26 int inframes
, outframes
;
28 int (*analyze
)(struct vf_priv_s
*, mp_image_t
*, mp_image_t
*);
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)) ) )
56 static inline void *my_memcpy_pic(void * dst
, void * src
, int bytesPerLine
, int height
, int dstStride
, int srcStride
)
61 for(i
=0; i
<height
; i
++)
63 memcpy(dst
, src
, bytesPerLine
);
71 static unsigned int hash_pic(unsigned char *img
, int w
, int h
, int stride
)
80 for (; x
<w
; x
+=step
) {
81 hash
= hash
^ (hash
<<4) ^ img
[x
];
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;
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];
107 m
->noise
+= abs(noise
);
108 m
->temp
+= abs(temp
);
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;
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
;
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
;
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
);
168 case -1: case 0: case 1: case 2:
178 static int analyze_aggressive(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
)
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
);
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");
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");
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 :) */
213 mp_msg(MSGT_VFILTER
, MSGL_V
, "mismatched telecine fields!\n");
218 if (2*m
.even
*m
.temp
< m
.odd
*m
.noise
) {
219 mp_msg(MSGT_VFILTER
, MSGL_V
, "caught telecine sync!\n");
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");
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");
239 if (4*m
.noise
> 5*m
.temp
) {
240 mp_msg(MSGT_VFILTER
, MSGL_V
, "merging fields out of sequence!\n");
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");
260 static void copy_image(mp_image_t
*dmpi
, mp_image_t
*mpi
, int field
)
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);
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);
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]);
305 static int do_put_image(struct vf_instance_s
* vf
, mp_image_t
*dmpi
)
307 struct vf_priv_s
*p
= vf
->priv
;
315 dropflag
= (++p
->lastdrop
>= 5);
318 dropflag
= (++p
->lastdrop
>= 5) && (4*p
->inframes
<= 5*p
->outframes
);
323 mp_msg(MSGT_VFILTER
, MSGL_V
, "drop! [%d/%d=%g]\n",
324 p
->outframes
, p
->inframes
, (float)p
->outframes
/p
->inframes
);
330 return vf_next_put_image(vf
, dmpi
);
333 static int put_image(struct vf_instance_s
* vf
, mp_image_t
*mpi
)
337 struct vf_priv_s
*p
= vf
->priv
;
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
)) {
352 /* Don't copy anything unless we'll need to read it. */
353 if (p
->needread
) copy_image(dmpi
, mpi
, 2);
357 /* Copy and display the whole frame. */
358 copy_image(dmpi
, mpi
, 2);
359 ret
= do_put_image(vf
, dmpi
);
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);
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);
377 static int query_format(struct vf_instance_s
* vf
, unsigned int fmt
)
379 /* FIXME - figure out which other formats work */
384 return vf_next_query_format(vf
, fmt
);
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
)
403 int (*func
)(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
);
406 { "fixed", analyze_fixed_pattern
, 0 },
407 { "aggressive", analyze_aggressive
, 1 },
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
)
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
)
432 for (args
=orig
=strdup(args
); args
; args
=next
) {
433 next
= strchr(args
, ':');
434 if (next
) *next
++ = 0;
440 static int open(vf_instance_t
*vf
, char* args
)
444 vf
->put_image
= put_image
;
445 vf
->query_format
= query_format
;
447 vf
->default_reqs
= VFCAP_ACCEPT_STRIDE
;
448 vf
->priv
= p
= calloc(1, sizeof(struct vf_priv_s
));
457 if (args
) parse_args(p
, args
);
458 p
->analyze
= anal_funcs
[p
->mode
].func
;
459 p
->needread
= anal_funcs
[p
->mode
].needread
;
463 vf_info_t vf_info_detc
= {
464 "de-telecine filter",