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 void block_diffs(struct metrics
*m
, unsigned char *old
, unsigned char *new, int os
, int ns
)
58 int x
, y
, even
=0, odd
=0, noise
, temp
;
59 unsigned char *oldp
, *newp
;
60 m
->noise
= m
->temp
= 0;
66 even
+= abs(newp
[0]-oldp
[0]);
67 odd
+= abs(newp
[ns
]-oldp
[os
]);
68 noise
+= newp
[ns
]-newp
[0];
69 temp
+= oldp
[os
]-newp
[0];
73 m
->noise
+= abs(noise
);
80 static void diff_planes(struct metrics
*m
, unsigned char *old
, unsigned char *new, int w
, int h
, int os
, int ns
)
82 int x
, y
, me
=0, mo
=0, mn
=0, mt
=0;
84 for (y
= 0; y
< h
-7; y
+= 8) {
85 for (x
= 0; x
< w
-7; x
+= 8) {
86 block_diffs(&l
, old
+x
+y
*os
, new+x
+y
*ns
, os
, ns
);
87 if (l
.even
> me
) me
= l
.even
;
88 if (l
.odd
> mo
) mo
= l
.odd
;
89 if (l
.noise
> mn
) mn
= l
.noise
;
90 if (l
.temp
> mt
) mt
= l
.temp
;
99 static void diff_fields(struct metrics
*metr
, mp_image_t
*old
, mp_image_t
*new)
101 struct metrics m
, mu
, mv
;
102 diff_planes(&m
, old
->planes
[0], new->planes
[0],
103 new->w
, new->h
, old
->stride
[0], new->stride
[0]);
104 if (new->flags
& MP_IMGFLAG_PLANAR
) {
105 diff_planes(&mu
, old
->planes
[1], new->planes
[1],
106 new->chroma_width
, new->chroma_height
,
107 old
->stride
[1], new->stride
[1]);
108 diff_planes(&mv
, old
->planes
[2], new->planes
[2],
109 new->chroma_width
, new->chroma_height
,
110 old
->stride
[2], new->stride
[2]);
111 if (mu
.even
> m
.even
) m
.even
= mu
.even
;
112 if (mu
.odd
> m
.odd
) m
.odd
= mu
.odd
;
113 if (mu
.noise
> m
.noise
) m
.noise
= mu
.noise
;
114 if (mu
.temp
> m
.temp
) m
.temp
= mu
.temp
;
115 if (mv
.even
> m
.even
) m
.even
= mv
.even
;
116 if (mv
.odd
> m
.odd
) m
.odd
= mv
.odd
;
117 if (mv
.noise
> m
.noise
) m
.noise
= mv
.noise
;
118 if (mv
.temp
> m
.temp
) m
.temp
= mv
.temp
;
123 static void status(int f
, struct metrics
*m
)
125 mp_msg(MSGT_VFILTER
, MSGL_V
, "frame %d: e=%d o=%d n=%d t=%d\n",
126 f
, m
->even
, m
->odd
, m
->noise
, m
->temp
);
129 static int analyze_fixed_pattern(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
)
131 if (p
->frame
>= 0) p
->frame
= (p
->frame
+1)%5;
132 mp_msg(MSGT_VFILTER
, MSGL_V
, "frame %d\n", p
->frame
);
134 case -1: case 0: case 1: case 2:
144 static int analyze_aggressive(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
)
146 struct metrics m
, pm
;
148 if (p
->frame
>= 0) p
->frame
= (p
->frame
+1)%5;
150 diff_fields(&m
, old
, new);
152 status(p
->frame
, &m
);
158 /* We need to break at scene changes, but is this a valid test? */
159 if ((m
.even
> p
->thres
[2]) && (m
.odd
> p
->thres
[2]) && (m
.temp
> p
->thres
[3])
160 && (m
.temp
> 5*pm
.temp
) && (m
.temp
*2 > m
.noise
)) {
161 mp_msg(MSGT_VFILTER
, MSGL_V
, "scene change breaking telecine!\n");
165 /* Thres. is to compensate for quantization errors when noise is low */
166 if (m
.noise
- m
.temp
> -p
->thres
[4]) {
167 if (COMPARABLE(m
.even
, pm
.odd
)) {
168 //mp_msg(MSGT_VFILTER, MSGL_V, "confirmed field match!\n");
170 } else if ((m
.even
< p
->thres
[0]) && (m
.odd
< p
->thres
[0]) && VERYCLOSE(m
.even
, m
.odd
)
171 && VERYCLOSE(m
.noise
,m
.temp
) && VERYCLOSE(m
.noise
,pm
.noise
)) {
172 mp_msg(MSGT_VFILTER
, MSGL_V
, "interlaced frame appears in duplicate!!!\n");
173 p
->pm
= pm
; /* hack :) */
178 mp_msg(MSGT_VFILTER
, MSGL_V
, "mismatched telecine fields!\n");
183 if (2*m
.even
*m
.temp
< m
.odd
*m
.noise
) {
184 mp_msg(MSGT_VFILTER
, MSGL_V
, "caught telecine sync!\n");
190 if (m
.noise
> p
->thres
[3]) {
191 if (m
.noise
> 2*m
.temp
) {
192 mp_msg(MSGT_VFILTER
, MSGL_V
, "merging fields out of sequence!\n");
195 if ((m
.noise
> 2*pm
.noise
) && (m
.even
> p
->thres
[2]) && (m
.odd
> p
->thres
[2])) {
196 mp_msg(MSGT_VFILTER
, MSGL_V
, "dropping horrible interlaced frame!\n");
204 if (4*m
.noise
> 5*m
.temp
) {
205 mp_msg(MSGT_VFILTER
, MSGL_V
, "merging fields out of sequence!\n");
213 if ((m
.even
> p
->thres
[1]) && (m
.even
> m
.odd
) && (m
.temp
> m
.noise
)) {
214 mp_msg(MSGT_VFILTER
, MSGL_V
, "lost telecine tracking!\n");
225 static void copy_image(mp_image_t
*dmpi
, mp_image_t
*mpi
, int field
)
229 my_memcpy_pic(dmpi
->planes
[0], mpi
->planes
[0], mpi
->w
, mpi
->h
/2,
230 dmpi
->stride
[0]*2, mpi
->stride
[0]*2);
231 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
232 my_memcpy_pic(dmpi
->planes
[1], mpi
->planes
[1],
233 mpi
->chroma_width
, mpi
->chroma_height
/2,
234 dmpi
->stride
[1]*2, mpi
->stride
[1]*2);
235 my_memcpy_pic(dmpi
->planes
[2], mpi
->planes
[2],
236 mpi
->chroma_width
, mpi
->chroma_height
/2,
237 dmpi
->stride
[2]*2, mpi
->stride
[2]*2);
241 my_memcpy_pic(dmpi
->planes
[0]+dmpi
->stride
[0],
242 mpi
->planes
[0]+mpi
->stride
[0], mpi
->w
, mpi
->h
/2,
243 dmpi
->stride
[0]*2, mpi
->stride
[0]*2);
244 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
245 my_memcpy_pic(dmpi
->planes
[1]+dmpi
->stride
[1],
246 mpi
->planes
[1]+mpi
->stride
[1],
247 mpi
->chroma_width
, mpi
->chroma_height
/2,
248 dmpi
->stride
[1]*2, mpi
->stride
[1]*2);
249 my_memcpy_pic(dmpi
->planes
[2]+dmpi
->stride
[2],
250 mpi
->planes
[2]+mpi
->stride
[2],
251 mpi
->chroma_width
, mpi
->chroma_height
/2,
252 dmpi
->stride
[2]*2, mpi
->stride
[2]*2);
256 memcpy_pic(dmpi
->planes
[0], mpi
->planes
[0], mpi
->w
, mpi
->h
,
257 dmpi
->stride
[0], mpi
->stride
[0]);
258 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
259 memcpy_pic(dmpi
->planes
[1], mpi
->planes
[1],
260 mpi
->chroma_width
, mpi
->chroma_height
,
261 dmpi
->stride
[1], mpi
->stride
[1]);
262 memcpy_pic(dmpi
->planes
[2], mpi
->planes
[2],
263 mpi
->chroma_width
, mpi
->chroma_height
,
264 dmpi
->stride
[2], mpi
->stride
[2]);
270 static int do_put_image(struct vf_instance
* vf
, mp_image_t
*dmpi
)
272 struct vf_priv_s
*p
= vf
->priv
;
280 dropflag
= (++p
->lastdrop
>= 5);
283 dropflag
= (++p
->lastdrop
>= 5) && (4*p
->inframes
<= 5*p
->outframes
);
288 mp_msg(MSGT_VFILTER
, MSGL_V
, "drop! [%d/%d=%g]\n",
289 p
->outframes
, p
->inframes
, (float)p
->outframes
/p
->inframes
);
295 return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
298 static int put_image(struct vf_instance
* vf
, mp_image_t
*mpi
, double pts
)
302 struct vf_priv_s
*p
= vf
->priv
;
306 if (p
->needread
) dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
,
307 MP_IMGTYPE_STATIC
, MP_IMGFLAG_ACCEPT_STRIDE
|
308 MP_IMGFLAG_PRESERVE
| MP_IMGFLAG_READABLE
,
309 mpi
->width
, mpi
->height
);
310 /* FIXME: is there a good way to get rid of static type? */
311 else dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
,
312 MP_IMGTYPE_STATIC
, MP_IMGFLAG_ACCEPT_STRIDE
|
313 MP_IMGFLAG_PRESERVE
, mpi
->width
, mpi
->height
);
315 switch (p
->analyze(p
, mpi
, dmpi
)) {
317 /* Don't copy anything unless we'll need to read it. */
318 if (p
->needread
) copy_image(dmpi
, mpi
, 2);
322 /* Copy and display the whole frame. */
323 copy_image(dmpi
, mpi
, 2);
324 ret
= do_put_image(vf
, dmpi
);
327 /* Only copy bottom field unless we need to read. */
328 if (p
->needread
) copy_image(dmpi
, mpi
, 2);
329 else copy_image(dmpi
, mpi
, 1);
333 /* Copy top field and show frame, then copy bottom if needed. */
334 copy_image(dmpi
, mpi
, 0);
335 ret
= do_put_image(vf
, dmpi
);
336 if (p
->needread
) copy_image(dmpi
, mpi
, 1);
342 static int query_format(struct vf_instance
* vf
, unsigned int fmt
)
344 /* FIXME - figure out which other formats work */
349 return vf_next_query_format(vf
, fmt
);
354 static int config(struct vf_instance
* vf
,
355 int width
, int height
, int d_width
, int d_height
,
356 unsigned int flags
, unsigned int outfmt
)
358 return vf_next_config(vf
,width
,height
,d_width
,d_height
,flags
,outfmt
);
361 static void uninit(struct vf_instance
* vf
)
368 int (*func
)(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
);
371 { "fixed", analyze_fixed_pattern
, 0 },
372 { "aggressive", analyze_aggressive
, 1 },
376 #define STARTVARS if (0)
377 #define GETVAR(str, name, out, func) \
378 else if (!strncmp((str), name "=", sizeof(name))) \
379 (out) = (func)((str) + sizeof(name))
381 static void parse_var(struct vf_priv_s
*p
, char *var
)
384 GETVAR(var
, "dr", p
->drop
, atoi
);
385 GETVAR(var
, "t0", p
->thres
[0], atoi
);
386 GETVAR(var
, "t1", p
->thres
[1], atoi
);
387 GETVAR(var
, "t2", p
->thres
[2], atoi
);
388 GETVAR(var
, "t3", p
->thres
[3], atoi
);
389 GETVAR(var
, "t4", p
->thres
[4], atoi
);
390 GETVAR(var
, "fr", p
->frame
, atoi
);
391 GETVAR(var
, "am", p
->mode
, atoi
);
394 static void parse_args(struct vf_priv_s
*p
, char *args
)
397 for (args
=orig
=strdup(args
); args
; args
=next
) {
398 next
= strchr(args
, ':');
399 if (next
) *next
++ = 0;
405 static int open(vf_instance_t
*vf
, char* args
)
409 vf
->put_image
= put_image
;
410 vf
->query_format
= query_format
;
412 vf
->default_reqs
= VFCAP_ACCEPT_STRIDE
;
413 vf
->priv
= p
= calloc(1, sizeof(struct vf_priv_s
));
422 if (args
) parse_args(p
, args
);
423 p
->analyze
= anal_funcs
[p
->mode
].func
;
424 p
->needread
= anal_funcs
[p
->mode
].needread
;
428 const vf_info_t vf_info_detc
= {
429 "de-telecine filter",