2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "img_format.h"
30 #include "libvo/fastmemcpy.h"
44 int inframes
, outframes
;
46 int (*analyze
)(struct vf_priv_s
*, mp_image_t
*, mp_image_t
*);
50 #define COMPE(a,b,e) (abs((a)-(b)) < (((a)+(b))>>(e)))
51 #define COMPARABLE(a,b) COMPE((a),(b),2)
52 #define VERYCLOSE(a,b) COMPE((a),(b),3)
54 #define OUTER_TC_NBHD(s) ( \
55 COMPARABLE((s)[-1].m.even,(s)[-1].m.odd) && \
56 COMPARABLE((s)[1].m.even,(s)[0].m.odd) && \
57 COMPARABLE((s)[2].m.even,(s)[1].m.odd) && \
58 COMPARABLE((s)[-1].m.noise,(s)[0].m.temp) && \
59 COMPARABLE((s)[2].m.noise,(s)[2].m.temp) )
61 #define INNER_TC_NBHD(s,l,h) ( \
62 COMPARABLE((s)[0].m.even,(l)) && \
63 COMPARABLE((s)[2].m.odd,(l)) && ( \
64 COMPARABLE((s)[0].m.noise,(h)) || \
65 COMPARABLE((s)[1].m.noise,(h)) ) )
74 static void block_diffs(struct metrics
*m
, unsigned char *old
, unsigned char *new, int os
, int ns
)
76 int x
, y
, even
=0, odd
=0, noise
, temp
;
77 unsigned char *oldp
, *newp
;
78 m
->noise
= m
->temp
= 0;
84 even
+= abs(newp
[0]-oldp
[0]);
85 odd
+= abs(newp
[ns
]-oldp
[os
]);
86 noise
+= newp
[ns
]-newp
[0];
87 temp
+= oldp
[os
]-newp
[0];
91 m
->noise
+= abs(noise
);
98 static void diff_planes(struct metrics
*m
, unsigned char *old
, unsigned char *new, int w
, int h
, int os
, int ns
)
100 int x
, y
, me
=0, mo
=0, mn
=0, mt
=0;
102 for (y
= 0; y
< h
-7; y
+= 8) {
103 for (x
= 0; x
< w
-7; x
+= 8) {
104 block_diffs(&l
, old
+x
+y
*os
, new+x
+y
*ns
, os
, ns
);
105 if (l
.even
> me
) me
= l
.even
;
106 if (l
.odd
> mo
) mo
= l
.odd
;
107 if (l
.noise
> mn
) mn
= l
.noise
;
108 if (l
.temp
> mt
) mt
= l
.temp
;
117 static void diff_fields(struct metrics
*metr
, mp_image_t
*old
, mp_image_t
*new)
119 struct metrics m
, mu
, mv
;
120 diff_planes(&m
, old
->planes
[0], new->planes
[0],
121 new->w
, new->h
, old
->stride
[0], new->stride
[0]);
122 if (new->flags
& MP_IMGFLAG_PLANAR
) {
123 diff_planes(&mu
, old
->planes
[1], new->planes
[1],
124 new->chroma_width
, new->chroma_height
,
125 old
->stride
[1], new->stride
[1]);
126 diff_planes(&mv
, old
->planes
[2], new->planes
[2],
127 new->chroma_width
, new->chroma_height
,
128 old
->stride
[2], new->stride
[2]);
129 if (mu
.even
> m
.even
) m
.even
= mu
.even
;
130 if (mu
.odd
> m
.odd
) m
.odd
= mu
.odd
;
131 if (mu
.noise
> m
.noise
) m
.noise
= mu
.noise
;
132 if (mu
.temp
> m
.temp
) m
.temp
= mu
.temp
;
133 if (mv
.even
> m
.even
) m
.even
= mv
.even
;
134 if (mv
.odd
> m
.odd
) m
.odd
= mv
.odd
;
135 if (mv
.noise
> m
.noise
) m
.noise
= mv
.noise
;
136 if (mv
.temp
> m
.temp
) m
.temp
= mv
.temp
;
141 static void status(int f
, struct metrics
*m
)
143 mp_msg(MSGT_VFILTER
, MSGL_V
, "frame %d: e=%d o=%d n=%d t=%d\n",
144 f
, m
->even
, m
->odd
, m
->noise
, m
->temp
);
147 static int analyze_fixed_pattern(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
)
149 if (p
->frame
>= 0) p
->frame
= (p
->frame
+1)%5;
150 mp_msg(MSGT_VFILTER
, MSGL_V
, "frame %d\n", p
->frame
);
152 case -1: case 0: case 1: case 2:
162 static int analyze_aggressive(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
)
164 struct metrics m
, pm
;
166 if (p
->frame
>= 0) p
->frame
= (p
->frame
+1)%5;
168 diff_fields(&m
, old
, new);
170 status(p
->frame
, &m
);
176 /* We need to break at scene changes, but is this a valid test? */
177 if ((m
.even
> p
->thres
[2]) && (m
.odd
> p
->thres
[2]) && (m
.temp
> p
->thres
[3])
178 && (m
.temp
> 5*pm
.temp
) && (m
.temp
*2 > m
.noise
)) {
179 mp_msg(MSGT_VFILTER
, MSGL_V
, "scene change breaking telecine!\n");
183 /* Thres. is to compensate for quantization errors when noise is low */
184 if (m
.noise
- m
.temp
> -p
->thres
[4]) {
185 if (COMPARABLE(m
.even
, pm
.odd
)) {
186 //mp_msg(MSGT_VFILTER, MSGL_V, "confirmed field match!\n");
188 } else if ((m
.even
< p
->thres
[0]) && (m
.odd
< p
->thres
[0]) && VERYCLOSE(m
.even
, m
.odd
)
189 && VERYCLOSE(m
.noise
,m
.temp
) && VERYCLOSE(m
.noise
,pm
.noise
)) {
190 mp_msg(MSGT_VFILTER
, MSGL_V
, "interlaced frame appears in duplicate!!!\n");
191 p
->pm
= pm
; /* hack :) */
196 mp_msg(MSGT_VFILTER
, MSGL_V
, "mismatched telecine fields!\n");
201 if (2*m
.even
*m
.temp
< m
.odd
*m
.noise
) {
202 mp_msg(MSGT_VFILTER
, MSGL_V
, "caught telecine sync!\n");
208 if (m
.noise
> p
->thres
[3]) {
209 if (m
.noise
> 2*m
.temp
) {
210 mp_msg(MSGT_VFILTER
, MSGL_V
, "merging fields out of sequence!\n");
213 if ((m
.noise
> 2*pm
.noise
) && (m
.even
> p
->thres
[2]) && (m
.odd
> p
->thres
[2])) {
214 mp_msg(MSGT_VFILTER
, MSGL_V
, "dropping horrible interlaced frame!\n");
222 if (4*m
.noise
> 5*m
.temp
) {
223 mp_msg(MSGT_VFILTER
, MSGL_V
, "merging fields out of sequence!\n");
231 if ((m
.even
> p
->thres
[1]) && (m
.even
> m
.odd
) && (m
.temp
> m
.noise
)) {
232 mp_msg(MSGT_VFILTER
, MSGL_V
, "lost telecine tracking!\n");
243 static void copy_image(mp_image_t
*dmpi
, mp_image_t
*mpi
, int field
)
247 my_memcpy_pic(dmpi
->planes
[0], mpi
->planes
[0], mpi
->w
, mpi
->h
/2,
248 dmpi
->stride
[0]*2, mpi
->stride
[0]*2);
249 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
250 my_memcpy_pic(dmpi
->planes
[1], mpi
->planes
[1],
251 mpi
->chroma_width
, mpi
->chroma_height
/2,
252 dmpi
->stride
[1]*2, mpi
->stride
[1]*2);
253 my_memcpy_pic(dmpi
->planes
[2], mpi
->planes
[2],
254 mpi
->chroma_width
, mpi
->chroma_height
/2,
255 dmpi
->stride
[2]*2, mpi
->stride
[2]*2);
259 my_memcpy_pic(dmpi
->planes
[0]+dmpi
->stride
[0],
260 mpi
->planes
[0]+mpi
->stride
[0], mpi
->w
, mpi
->h
/2,
261 dmpi
->stride
[0]*2, mpi
->stride
[0]*2);
262 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
263 my_memcpy_pic(dmpi
->planes
[1]+dmpi
->stride
[1],
264 mpi
->planes
[1]+mpi
->stride
[1],
265 mpi
->chroma_width
, mpi
->chroma_height
/2,
266 dmpi
->stride
[1]*2, mpi
->stride
[1]*2);
267 my_memcpy_pic(dmpi
->planes
[2]+dmpi
->stride
[2],
268 mpi
->planes
[2]+mpi
->stride
[2],
269 mpi
->chroma_width
, mpi
->chroma_height
/2,
270 dmpi
->stride
[2]*2, mpi
->stride
[2]*2);
274 memcpy_pic(dmpi
->planes
[0], mpi
->planes
[0], mpi
->w
, mpi
->h
,
275 dmpi
->stride
[0], mpi
->stride
[0]);
276 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
277 memcpy_pic(dmpi
->planes
[1], mpi
->planes
[1],
278 mpi
->chroma_width
, mpi
->chroma_height
,
279 dmpi
->stride
[1], mpi
->stride
[1]);
280 memcpy_pic(dmpi
->planes
[2], mpi
->planes
[2],
281 mpi
->chroma_width
, mpi
->chroma_height
,
282 dmpi
->stride
[2], mpi
->stride
[2]);
288 static int do_put_image(struct vf_instance
*vf
, mp_image_t
*dmpi
)
290 struct vf_priv_s
*p
= vf
->priv
;
298 dropflag
= (++p
->lastdrop
>= 5);
301 dropflag
= (++p
->lastdrop
>= 5) && (4*p
->inframes
<= 5*p
->outframes
);
306 mp_msg(MSGT_VFILTER
, MSGL_V
, "drop! [%d/%d=%g]\n",
307 p
->outframes
, p
->inframes
, (float)p
->outframes
/p
->inframes
);
313 return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
316 static int put_image(struct vf_instance
*vf
, mp_image_t
*mpi
, double pts
)
320 struct vf_priv_s
*p
= vf
->priv
;
324 if (p
->needread
) dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
,
325 MP_IMGTYPE_STATIC
, MP_IMGFLAG_ACCEPT_STRIDE
|
326 MP_IMGFLAG_PRESERVE
| MP_IMGFLAG_READABLE
,
327 mpi
->width
, mpi
->height
);
328 /* FIXME: is there a good way to get rid of static type? */
329 else dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
,
330 MP_IMGTYPE_STATIC
, MP_IMGFLAG_ACCEPT_STRIDE
|
331 MP_IMGFLAG_PRESERVE
, mpi
->width
, mpi
->height
);
333 switch (p
->analyze(p
, mpi
, dmpi
)) {
335 /* Don't copy anything unless we'll need to read it. */
336 if (p
->needread
) copy_image(dmpi
, mpi
, 2);
340 /* Copy and display the whole frame. */
341 copy_image(dmpi
, mpi
, 2);
342 ret
= do_put_image(vf
, dmpi
);
345 /* Only copy bottom field unless we need to read. */
346 if (p
->needread
) copy_image(dmpi
, mpi
, 2);
347 else copy_image(dmpi
, mpi
, 1);
351 /* Copy top field and show frame, then copy bottom if needed. */
352 copy_image(dmpi
, mpi
, 0);
353 ret
= do_put_image(vf
, dmpi
);
354 if (p
->needread
) copy_image(dmpi
, mpi
, 1);
360 static int query_format(struct vf_instance
*vf
, unsigned int fmt
)
362 /* FIXME - figure out which other formats work */
367 return vf_next_query_format(vf
, fmt
);
372 static int config(struct vf_instance
*vf
,
373 int width
, int height
, int d_width
, int d_height
,
374 unsigned int flags
, unsigned int outfmt
)
376 return vf_next_config(vf
,width
,height
,d_width
,d_height
,flags
,outfmt
);
379 static void uninit(struct vf_instance
*vf
)
386 int (*func
)(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
);
389 { "fixed", analyze_fixed_pattern
, 0 },
390 { "aggressive", analyze_aggressive
, 1 },
394 #define STARTVARS if (0)
395 #define GETVAR(str, name, out, func) \
396 else if (!strncmp((str), name "=", sizeof(name))) \
397 (out) = (func)((str) + sizeof(name))
399 static void parse_var(struct vf_priv_s
*p
, char *var
)
402 GETVAR(var
, "dr", p
->drop
, atoi
);
403 GETVAR(var
, "t0", p
->thres
[0], atoi
);
404 GETVAR(var
, "t1", p
->thres
[1], atoi
);
405 GETVAR(var
, "t2", p
->thres
[2], atoi
);
406 GETVAR(var
, "t3", p
->thres
[3], atoi
);
407 GETVAR(var
, "t4", p
->thres
[4], atoi
);
408 GETVAR(var
, "fr", p
->frame
, atoi
);
409 GETVAR(var
, "am", p
->mode
, atoi
);
412 static void parse_args(struct vf_priv_s
*p
, char *args
)
415 for (args
=orig
=strdup(args
); args
; args
=next
) {
416 next
= strchr(args
, ':');
417 if (next
) *next
++ = 0;
423 static int vf_open(vf_instance_t
*vf
, char *args
)
427 vf
->put_image
= put_image
;
428 vf
->query_format
= query_format
;
430 vf
->default_reqs
= VFCAP_ACCEPT_STRIDE
;
431 vf
->priv
= p
= calloc(1, sizeof(struct vf_priv_s
));
440 if (args
) parse_args(p
, args
);
441 p
->analyze
= anal_funcs
[p
->mode
].func
;
442 p
->needread
= anal_funcs
[p
->mode
].needread
;
446 const vf_info_t vf_info_detc
= {
447 "de-telecine filter",