2 * Copyright (C) 2012 Michael Niedermayer <michaelni@gmx.at>
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <float.h> /* FLT_MAX */
23 #include "libavutil/cpu.h"
24 #include "libavutil/common.h"
25 #include "libavutil/opt.h"
29 #define OFFSET(x) offsetof(IDETContext, x)
30 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
32 static const AVOption idet_options
[] = {
33 { "intl_thres", "set interlacing threshold", OFFSET(interlace_threshold
), AV_OPT_TYPE_FLOAT
, {.dbl
= 1.04}, -1, FLT_MAX
, FLAGS
},
34 { "prog_thres", "set progressive threshold", OFFSET(progressive_threshold
), AV_OPT_TYPE_FLOAT
, {.dbl
= 1.5}, -1, FLT_MAX
, FLAGS
},
35 { "rep_thres", "set repeat threshold", OFFSET(repeat_threshold
), AV_OPT_TYPE_FLOAT
, {.dbl
= 3.0}, -1, FLT_MAX
, FLAGS
},
36 { "half_life", "half life of cumulative statistics", OFFSET(half_life
), AV_OPT_TYPE_FLOAT
, {.dbl
= 0.0}, -1, INT_MAX
, FLAGS
},
40 AVFILTER_DEFINE_CLASS(idet
);
42 static const char *type2str(Type type
)
45 case TFF
: return "tff";
46 case BFF
: return "bff";
47 case PROGRESSIVE
: return "progressive";
48 case UNDETERMINED
: return "undetermined";
53 #define PRECISION 1048576
55 static uint64_t uintpow(uint64_t b
,unsigned int e
)
62 static int av_dict_set_fxp(AVDictionary
**pm
, const char *key
, uint64_t value
, unsigned int digits
,
66 uint64_t print_precision
= uintpow(10, digits
);
68 value
= av_rescale(value
, print_precision
, PRECISION
);
70 snprintf(valuestr
, sizeof(valuestr
), "%"PRId64
".%0*"PRId64
,
71 value
/ print_precision
, digits
, value
% print_precision
);
73 return av_dict_set(pm
, key
, valuestr
, flags
);
76 static const char *rep2str(RepeatedField repeated_field
)
78 switch(repeated_field
) {
79 case REPEAT_NONE
: return "neither";
80 case REPEAT_TOP
: return "top";
81 case REPEAT_BOTTOM
: return "bottom";
86 int ff_idet_filter_line_c(const uint8_t *a
, const uint8_t *b
, const uint8_t *c
, int w
)
92 int v
= (*a
++ + *c
++) - 2 * *b
++;
99 int ff_idet_filter_line_c_16bit(const uint16_t *a
, const uint16_t *b
, const uint16_t *c
, int w
)
105 int v
= (*a
++ + *c
++) - 2 * *b
++;
112 static void filter(AVFilterContext
*ctx
)
114 IDETContext
*idet
= ctx
->priv
;
116 int64_t alpha
[2]={0};
118 int64_t gamma
[2]={0};
119 Type type
, best_type
;
120 RepeatedField repeat
;
122 AVDictionary
**metadata
= avpriv_frame_get_metadatap(idet
->cur
);
124 for (i
= 0; i
< idet
->csp
->nb_components
; i
++) {
125 int w
= idet
->cur
->width
;
126 int h
= idet
->cur
->height
;
127 int refs
= idet
->cur
->linesize
[i
];
130 w
= FF_CEIL_RSHIFT(w
, idet
->csp
->log2_chroma_w
);
131 h
= FF_CEIL_RSHIFT(h
, idet
->csp
->log2_chroma_h
);
134 for (y
= 2; y
< h
- 2; y
++) {
135 uint8_t *prev
= &idet
->prev
->data
[i
][y
*refs
];
136 uint8_t *cur
= &idet
->cur
->data
[i
][y
*refs
];
137 uint8_t *next
= &idet
->next
->data
[i
][y
*refs
];
138 alpha
[ y
&1] += idet
->filter_line(cur
-refs
, prev
, cur
+refs
, w
);
139 alpha
[(y
^1)&1] += idet
->filter_line(cur
-refs
, next
, cur
+refs
, w
);
140 delta
+= idet
->filter_line(cur
-refs
, cur
, cur
+refs
, w
);
141 gamma
[(y
^1)&1] += idet
->filter_line(cur
, prev
, cur
, w
);
145 if (alpha
[0] > idet
->interlace_threshold
* alpha
[1]){
147 }else if(alpha
[1] > idet
->interlace_threshold
* alpha
[0]){
149 }else if(alpha
[1] > idet
->progressive_threshold
* delta
){
155 if ( gamma
[0] > idet
->repeat_threshold
* gamma
[1] ){
157 } else if ( gamma
[1] > idet
->repeat_threshold
* gamma
[0] ){
158 repeat
= REPEAT_BOTTOM
;
160 repeat
= REPEAT_NONE
;
163 memmove(idet
->history
+1, idet
->history
, HIST_SIZE
-1);
164 idet
->history
[0] = type
;
165 best_type
= UNDETERMINED
;
166 for(i
=0; i
<HIST_SIZE
; i
++){
167 if(idet
->history
[i
] != UNDETERMINED
){
168 if(best_type
== UNDETERMINED
)
169 best_type
= idet
->history
[i
];
171 if(idet
->history
[i
] == best_type
) {
179 if(idet
->last_type
== UNDETERMINED
){
180 if(match
) idet
->last_type
= best_type
;
182 if(match
>2) idet
->last_type
= best_type
;
185 if (idet
->last_type
== TFF
){
186 idet
->cur
->top_field_first
= 1;
187 idet
->cur
->interlaced_frame
= 1;
188 }else if(idet
->last_type
== BFF
){
189 idet
->cur
->top_field_first
= 0;
190 idet
->cur
->interlaced_frame
= 1;
191 }else if(idet
->last_type
== PROGRESSIVE
){
192 idet
->cur
->interlaced_frame
= 0;
196 idet
->repeats
[i
] = av_rescale(idet
->repeats
[i
], idet
->decay_coefficient
, PRECISION
);
199 idet
->prestat
[i
] = av_rescale(idet
->prestat
[i
], idet
->decay_coefficient
, PRECISION
);
200 idet
->poststat
[i
] = av_rescale(idet
->poststat
[i
], idet
->decay_coefficient
, PRECISION
);
203 idet
->total_repeats
[ repeat
] ++;
204 idet
->repeats
[ repeat
] += PRECISION
;
206 idet
->total_prestat
[ type
] ++;
207 idet
->prestat
[ type
] += PRECISION
;
209 idet
->total_poststat
[idet
->last_type
] ++;
210 idet
->poststat
[idet
->last_type
] += PRECISION
;
212 av_log(ctx
, AV_LOG_DEBUG
, "Repeated Field:%12s, Single frame:%12s, Multi frame:%12s\n",
213 rep2str(repeat
), type2str(type
), type2str(idet
->last_type
));
215 av_dict_set (metadata
, "lavfi.idet.repeated.current_frame", rep2str(repeat
), 0);
216 av_dict_set_fxp(metadata
, "lavfi.idet.repeated.neither", idet
->repeats
[REPEAT_NONE
], 2, 0);
217 av_dict_set_fxp(metadata
, "lavfi.idet.repeated.top", idet
->repeats
[REPEAT_TOP
], 2, 0);
218 av_dict_set_fxp(metadata
, "lavfi.idet.repeated.bottom", idet
->repeats
[REPEAT_BOTTOM
], 2, 0);
220 av_dict_set (metadata
, "lavfi.idet.single.current_frame", type2str(type
), 0);
221 av_dict_set_fxp(metadata
, "lavfi.idet.single.tff", idet
->prestat
[TFF
], 2 , 0);
222 av_dict_set_fxp(metadata
, "lavfi.idet.single.bff", idet
->prestat
[BFF
], 2, 0);
223 av_dict_set_fxp(metadata
, "lavfi.idet.single.progressive", idet
->prestat
[PROGRESSIVE
], 2, 0);
224 av_dict_set_fxp(metadata
, "lavfi.idet.single.undetermined", idet
->prestat
[UNDETERMINED
], 2, 0);
226 av_dict_set (metadata
, "lavfi.idet.multiple.current_frame", type2str(idet
->last_type
), 0);
227 av_dict_set_fxp(metadata
, "lavfi.idet.multiple.tff", idet
->poststat
[TFF
], 2, 0);
228 av_dict_set_fxp(metadata
, "lavfi.idet.multiple.bff", idet
->poststat
[BFF
], 2, 0);
229 av_dict_set_fxp(metadata
, "lavfi.idet.multiple.progressive", idet
->poststat
[PROGRESSIVE
], 2, 0);
230 av_dict_set_fxp(metadata
, "lavfi.idet.multiple.undetermined", idet
->poststat
[UNDETERMINED
], 2, 0);
233 static int filter_frame(AVFilterLink
*link
, AVFrame
*picref
)
235 AVFilterContext
*ctx
= link
->dst
;
236 IDETContext
*idet
= ctx
->priv
;
239 av_frame_free(&idet
->prev
);
240 idet
->prev
= idet
->cur
;
241 idet
->cur
= idet
->next
;
245 !(idet
->cur
= av_frame_clone(idet
->next
)))
246 return AVERROR(ENOMEM
);
252 idet
->csp
= av_pix_fmt_desc_get(link
->format
);
253 if (idet
->csp
->comp
[0].depth_minus1
/ 8 == 1){
254 idet
->filter_line
= (ff_idet_filter_func
)ff_idet_filter_line_c_16bit
;
256 ff_idet_init_x86(idet
, 1);
261 return ff_filter_frame(ctx
->outputs
[0], av_frame_clone(idet
->cur
));
264 static int request_frame(AVFilterLink
*link
)
266 AVFilterContext
*ctx
= link
->src
;
267 IDETContext
*idet
= ctx
->priv
;
275 ret
= ff_request_frame(link
->src
->inputs
[0]);
277 if (ret
== AVERROR_EOF
&& idet
->cur
) {
278 AVFrame
*next
= av_frame_clone(idet
->next
);
281 return AVERROR(ENOMEM
);
283 filter_frame(link
->src
->inputs
[0], next
);
285 } else if (ret
< 0) {
288 } while (!idet
->prev
);
293 static av_cold
void uninit(AVFilterContext
*ctx
)
295 IDETContext
*idet
= ctx
->priv
;
297 av_log(ctx
, AV_LOG_INFO
, "Repeated Fields: Neither:%6"PRId64
" Top:%6"PRId64
" Bottom:%6"PRId64
"\n",
298 idet
->total_repeats
[REPEAT_NONE
],
299 idet
->total_repeats
[REPEAT_TOP
],
300 idet
->total_repeats
[REPEAT_BOTTOM
]
302 av_log(ctx
, AV_LOG_INFO
, "Single frame detection: TFF:%6"PRId64
" BFF:%6"PRId64
" Progressive:%6"PRId64
" Undetermined:%6"PRId64
"\n",
303 idet
->total_prestat
[TFF
],
304 idet
->total_prestat
[BFF
],
305 idet
->total_prestat
[PROGRESSIVE
],
306 idet
->total_prestat
[UNDETERMINED
]
308 av_log(ctx
, AV_LOG_INFO
, "Multi frame detection: TFF:%6"PRId64
" BFF:%6"PRId64
" Progressive:%6"PRId64
" Undetermined:%6"PRId64
"\n",
309 idet
->total_poststat
[TFF
],
310 idet
->total_poststat
[BFF
],
311 idet
->total_poststat
[PROGRESSIVE
],
312 idet
->total_poststat
[UNDETERMINED
]
315 av_frame_free(&idet
->prev
);
316 av_frame_free(&idet
->cur
);
317 av_frame_free(&idet
->next
);
320 static int query_formats(AVFilterContext
*ctx
)
322 static const enum AVPixelFormat pix_fmts
[] = {
335 AV_PIX_FMT_YUV420P10
,
336 AV_PIX_FMT_YUV422P10
,
337 AV_PIX_FMT_YUV444P10
,
338 AV_PIX_FMT_YUV420P16
,
339 AV_PIX_FMT_YUV422P16
,
340 AV_PIX_FMT_YUV444P16
,
345 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
350 static int config_output(AVFilterLink
*outlink
)
352 outlink
->flags
|= FF_LINK_FLAG_REQUEST_LOOP
;
356 static av_cold
int init(AVFilterContext
*ctx
)
358 IDETContext
*idet
= ctx
->priv
;
361 idet
->last_type
= UNDETERMINED
;
362 memset(idet
->history
, UNDETERMINED
, HIST_SIZE
);
364 if( idet
->half_life
> 0 )
365 idet
->decay_coefficient
= (uint64_t) round( PRECISION
* exp2(-1.0 / idet
->half_life
) );
367 idet
->decay_coefficient
= PRECISION
;
369 idet
->filter_line
= ff_idet_filter_line_c
;
372 ff_idet_init_x86(idet
, 0);
377 static const AVFilterPad idet_inputs
[] = {
380 .type
= AVMEDIA_TYPE_VIDEO
,
381 .filter_frame
= filter_frame
,
386 static const AVFilterPad idet_outputs
[] = {
389 .type
= AVMEDIA_TYPE_VIDEO
,
390 .config_props
= config_output
,
391 .request_frame
= request_frame
396 AVFilter ff_vf_idet
= {
398 .description
= NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
399 .priv_size
= sizeof(IDETContext
),
402 .query_formats
= query_formats
,
403 .inputs
= idet_inputs
,
404 .outputs
= idet_outputs
,
405 .priv_class
= &idet_class
,