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
*);
48 struct vf_detc_pts_buf ptsbuf
;
51 #define COMPE(a,b,e) (abs((a)-(b)) < (((a)+(b))>>(e)))
52 #define COMPARABLE(a,b) COMPE((a),(b),2)
53 #define VERYCLOSE(a,b) COMPE((a),(b),3)
55 #define OUTER_TC_NBHD(s) ( \
56 COMPARABLE((s)[-1].m.even,(s)[-1].m.odd) && \
57 COMPARABLE((s)[1].m.even,(s)[0].m.odd) && \
58 COMPARABLE((s)[2].m.even,(s)[1].m.odd) && \
59 COMPARABLE((s)[-1].m.noise,(s)[0].m.temp) && \
60 COMPARABLE((s)[2].m.noise,(s)[2].m.temp) )
62 #define INNER_TC_NBHD(s,l,h) ( \
63 COMPARABLE((s)[0].m.even,(l)) && \
64 COMPARABLE((s)[2].m.odd,(l)) && ( \
65 COMPARABLE((s)[0].m.noise,(h)) || \
66 COMPARABLE((s)[1].m.noise,(h)) ) )
75 static void block_diffs(struct metrics
*m
, unsigned char *old
, unsigned char *new, int os
, int ns
)
77 int x
, y
, even
=0, odd
=0, noise
, temp
;
78 unsigned char *oldp
, *newp
;
79 m
->noise
= m
->temp
= 0;
85 even
+= abs(newp
[0]-oldp
[0]);
86 odd
+= abs(newp
[ns
]-oldp
[os
]);
87 noise
+= newp
[ns
]-newp
[0];
88 temp
+= oldp
[os
]-newp
[0];
92 m
->noise
+= abs(noise
);
99 static void diff_planes(struct metrics
*m
, unsigned char *old
, unsigned char *new, int w
, int h
, int os
, int ns
)
101 int x
, y
, me
=0, mo
=0, mn
=0, mt
=0;
103 for (y
= 0; y
< h
-7; y
+= 8) {
104 for (x
= 0; x
< w
-7; x
+= 8) {
105 block_diffs(&l
, old
+x
+y
*os
, new+x
+y
*ns
, os
, ns
);
106 if (l
.even
> me
) me
= l
.even
;
107 if (l
.odd
> mo
) mo
= l
.odd
;
108 if (l
.noise
> mn
) mn
= l
.noise
;
109 if (l
.temp
> mt
) mt
= l
.temp
;
118 static void diff_fields(struct metrics
*metr
, mp_image_t
*old
, mp_image_t
*new)
120 struct metrics m
, mu
, mv
;
121 diff_planes(&m
, old
->planes
[0], new->planes
[0],
122 new->w
, new->h
, old
->stride
[0], new->stride
[0]);
123 if (new->flags
& MP_IMGFLAG_PLANAR
) {
124 diff_planes(&mu
, old
->planes
[1], new->planes
[1],
125 new->chroma_width
, new->chroma_height
,
126 old
->stride
[1], new->stride
[1]);
127 diff_planes(&mv
, old
->planes
[2], new->planes
[2],
128 new->chroma_width
, new->chroma_height
,
129 old
->stride
[2], new->stride
[2]);
130 if (mu
.even
> m
.even
) m
.even
= mu
.even
;
131 if (mu
.odd
> m
.odd
) m
.odd
= mu
.odd
;
132 if (mu
.noise
> m
.noise
) m
.noise
= mu
.noise
;
133 if (mu
.temp
> m
.temp
) m
.temp
= mu
.temp
;
134 if (mv
.even
> m
.even
) m
.even
= mv
.even
;
135 if (mv
.odd
> m
.odd
) m
.odd
= mv
.odd
;
136 if (mv
.noise
> m
.noise
) m
.noise
= mv
.noise
;
137 if (mv
.temp
> m
.temp
) m
.temp
= mv
.temp
;
142 static void status(int f
, struct metrics
*m
)
144 mp_msg(MSGT_VFILTER
, MSGL_V
, "frame %d: e=%d o=%d n=%d t=%d\n",
145 f
, m
->even
, m
->odd
, m
->noise
, m
->temp
);
148 static int analyze_fixed_pattern(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
)
150 if (p
->frame
>= 0) p
->frame
= (p
->frame
+1)%5;
151 mp_msg(MSGT_VFILTER
, MSGL_V
, "frame %d\n", p
->frame
);
153 case -1: case 0: case 1: case 2:
163 static int analyze_aggressive(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
)
165 struct metrics m
, pm
;
167 if (p
->frame
>= 0) p
->frame
= (p
->frame
+1)%5;
169 diff_fields(&m
, old
, new);
171 status(p
->frame
, &m
);
177 /* We need to break at scene changes, but is this a valid test? */
178 if ((m
.even
> p
->thres
[2]) && (m
.odd
> p
->thres
[2]) && (m
.temp
> p
->thres
[3])
179 && (m
.temp
> 5*pm
.temp
) && (m
.temp
*2 > m
.noise
)) {
180 mp_msg(MSGT_VFILTER
, MSGL_V
, "scene change breaking telecine!\n");
184 /* Thres. is to compensate for quantization errors when noise is low */
185 if (m
.noise
- m
.temp
> -p
->thres
[4]) {
186 if (COMPARABLE(m
.even
, pm
.odd
)) {
187 //mp_msg(MSGT_VFILTER, MSGL_V, "confirmed field match!\n");
189 } else if ((m
.even
< p
->thres
[0]) && (m
.odd
< p
->thres
[0]) && VERYCLOSE(m
.even
, m
.odd
)
190 && VERYCLOSE(m
.noise
,m
.temp
) && VERYCLOSE(m
.noise
,pm
.noise
)) {
191 mp_msg(MSGT_VFILTER
, MSGL_V
, "interlaced frame appears in duplicate!!!\n");
192 p
->pm
= pm
; /* hack :) */
197 mp_msg(MSGT_VFILTER
, MSGL_V
, "mismatched telecine fields!\n");
202 if (2*m
.even
*m
.temp
< m
.odd
*m
.noise
) {
203 mp_msg(MSGT_VFILTER
, MSGL_V
, "caught telecine sync!\n");
209 if (m
.noise
> p
->thres
[3]) {
210 if (m
.noise
> 2*m
.temp
) {
211 mp_msg(MSGT_VFILTER
, MSGL_V
, "merging fields out of sequence!\n");
214 if ((m
.noise
> 2*pm
.noise
) && (m
.even
> p
->thres
[2]) && (m
.odd
> p
->thres
[2])) {
215 mp_msg(MSGT_VFILTER
, MSGL_V
, "dropping horrible interlaced frame!\n");
223 if (4*m
.noise
> 5*m
.temp
) {
224 mp_msg(MSGT_VFILTER
, MSGL_V
, "merging fields out of sequence!\n");
232 if ((m
.even
> p
->thres
[1]) && (m
.even
> m
.odd
) && (m
.temp
> m
.noise
)) {
233 mp_msg(MSGT_VFILTER
, MSGL_V
, "lost telecine tracking!\n");
244 static void copy_image(mp_image_t
*dmpi
, mp_image_t
*mpi
, int field
)
248 my_memcpy_pic(dmpi
->planes
[0], mpi
->planes
[0], mpi
->w
, mpi
->h
/2,
249 dmpi
->stride
[0]*2, mpi
->stride
[0]*2);
250 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
251 my_memcpy_pic(dmpi
->planes
[1], mpi
->planes
[1],
252 mpi
->chroma_width
, mpi
->chroma_height
/2,
253 dmpi
->stride
[1]*2, mpi
->stride
[1]*2);
254 my_memcpy_pic(dmpi
->planes
[2], mpi
->planes
[2],
255 mpi
->chroma_width
, mpi
->chroma_height
/2,
256 dmpi
->stride
[2]*2, mpi
->stride
[2]*2);
260 my_memcpy_pic(dmpi
->planes
[0]+dmpi
->stride
[0],
261 mpi
->planes
[0]+mpi
->stride
[0], mpi
->w
, mpi
->h
/2,
262 dmpi
->stride
[0]*2, mpi
->stride
[0]*2);
263 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
264 my_memcpy_pic(dmpi
->planes
[1]+dmpi
->stride
[1],
265 mpi
->planes
[1]+mpi
->stride
[1],
266 mpi
->chroma_width
, mpi
->chroma_height
/2,
267 dmpi
->stride
[1]*2, mpi
->stride
[1]*2);
268 my_memcpy_pic(dmpi
->planes
[2]+dmpi
->stride
[2],
269 mpi
->planes
[2]+mpi
->stride
[2],
270 mpi
->chroma_width
, mpi
->chroma_height
/2,
271 dmpi
->stride
[2]*2, mpi
->stride
[2]*2);
275 memcpy_pic(dmpi
->planes
[0], mpi
->planes
[0], mpi
->w
, mpi
->h
,
276 dmpi
->stride
[0], mpi
->stride
[0]);
277 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
278 memcpy_pic(dmpi
->planes
[1], mpi
->planes
[1],
279 mpi
->chroma_width
, mpi
->chroma_height
,
280 dmpi
->stride
[1], mpi
->stride
[1]);
281 memcpy_pic(dmpi
->planes
[2], mpi
->planes
[2],
282 mpi
->chroma_width
, mpi
->chroma_height
,
283 dmpi
->stride
[2], mpi
->stride
[2]);
289 static int do_put_image(struct vf_instance
*vf
, mp_image_t
*dmpi
, double pts
)
291 struct vf_priv_s
*p
= vf
->priv
;
299 dropflag
= (++p
->lastdrop
>= 5);
302 dropflag
= (++p
->lastdrop
>= 5) && (4*p
->inframes
<= 5*p
->outframes
);
307 mp_msg(MSGT_VFILTER
, MSGL_V
, "drop! [%d/%d=%g]\n",
308 p
->outframes
, p
->inframes
, (float)p
->outframes
/p
->inframes
);
310 vf_detc_adjust_pts(&p
->ptsbuf
, pts
, 0, 1);
315 return vf_next_put_image(vf
, dmpi
, vf_detc_adjust_pts(&p
->ptsbuf
, pts
, 0, 0));
318 static int put_image(struct vf_instance
*vf
, mp_image_t
*mpi
, double pts
)
322 struct vf_priv_s
*p
= vf
->priv
;
326 if (p
->needread
) dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
,
327 MP_IMGTYPE_STATIC
, MP_IMGFLAG_ACCEPT_STRIDE
|
328 MP_IMGFLAG_PRESERVE
| MP_IMGFLAG_READABLE
,
329 mpi
->width
, mpi
->height
);
330 /* FIXME: is there a good way to get rid of static type? */
331 else dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
,
332 MP_IMGTYPE_STATIC
, MP_IMGFLAG_ACCEPT_STRIDE
|
333 MP_IMGFLAG_PRESERVE
, mpi
->width
, mpi
->height
);
335 switch (p
->analyze(p
, mpi
, dmpi
)) {
337 /* Don't copy anything unless we'll need to read it. */
338 if (p
->needread
) copy_image(dmpi
, mpi
, 2);
340 vf_detc_adjust_pts(&p
->ptsbuf
, pts
, 0, 1);
343 /* Copy and display the whole frame. */
344 copy_image(dmpi
, mpi
, 2);
345 ret
= do_put_image(vf
, dmpi
, pts
);
348 /* Only copy bottom field unless we need to read. */
349 if (p
->needread
) copy_image(dmpi
, mpi
, 2);
350 else copy_image(dmpi
, mpi
, 1);
352 vf_detc_adjust_pts(&p
->ptsbuf
, pts
, 0, 1);
355 /* Copy top field and show frame, then copy bottom if needed. */
356 copy_image(dmpi
, mpi
, 0);
357 ret
= do_put_image(vf
, dmpi
, pts
);
358 if (p
->needread
) copy_image(dmpi
, mpi
, 1);
364 static int query_format(struct vf_instance
*vf
, unsigned int fmt
)
366 /* FIXME - figure out which other formats work */
371 return vf_next_query_format(vf
, fmt
);
376 static int config(struct vf_instance
*vf
,
377 int width
, int height
, int d_width
, int d_height
,
378 unsigned int flags
, unsigned int outfmt
)
380 return vf_next_config(vf
,width
,height
,d_width
,d_height
,flags
,outfmt
);
383 static void uninit(struct vf_instance
*vf
)
390 int (*func
)(struct vf_priv_s
*p
, mp_image_t
*new, mp_image_t
*old
);
393 { "fixed", analyze_fixed_pattern
, 0 },
394 { "aggressive", analyze_aggressive
, 1 },
398 #define STARTVARS if (0)
399 #define GETVAR(str, name, out, func) \
400 else if (!strncmp((str), name "=", sizeof(name))) \
401 (out) = (func)((str) + sizeof(name))
403 static void parse_var(struct vf_priv_s
*p
, char *var
)
406 GETVAR(var
, "dr", p
->drop
, atoi
);
407 GETVAR(var
, "t0", p
->thres
[0], atoi
);
408 GETVAR(var
, "t1", p
->thres
[1], atoi
);
409 GETVAR(var
, "t2", p
->thres
[2], atoi
);
410 GETVAR(var
, "t3", p
->thres
[3], atoi
);
411 GETVAR(var
, "t4", p
->thres
[4], atoi
);
412 GETVAR(var
, "fr", p
->frame
, atoi
);
413 GETVAR(var
, "am", p
->mode
, atoi
);
416 static void parse_args(struct vf_priv_s
*p
, char *args
)
419 for (args
=orig
=strdup(args
); args
; args
=next
) {
420 next
= strchr(args
, ':');
421 if (next
) *next
++ = 0;
427 static int vf_open(vf_instance_t
*vf
, char *args
)
431 vf
->put_image
= put_image
;
432 vf
->query_format
= query_format
;
434 vf
->default_reqs
= VFCAP_ACCEPT_STRIDE
;
435 vf
->priv
= p
= calloc(1, sizeof(struct vf_priv_s
));
444 if (args
) parse_args(p
, args
);
445 p
->analyze
= anal_funcs
[p
->mode
].func
;
446 p
->needread
= anal_funcs
[p
->mode
].needread
;
447 vf_detc_init_pts_buf(&p
->ptsbuf
);
451 const vf_info_t vf_info_detc
= {
452 "de-telecine filter",