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
},
38 AVFILTER_DEFINE_CLASS(idet
);
40 static const char *type2str(Type type
)
43 case TFF
: return "Top Field First ";
44 case BFF
: return "Bottom Field First";
45 case PROGRSSIVE
: return "Progressive ";
46 case UNDETERMINED
: return "Undetermined ";
51 int ff_idet_filter_line_c(const uint8_t *a
, const uint8_t *b
, const uint8_t *c
, int w
)
57 int v
= (*a
++ + *c
++) - 2 * *b
++;
64 int ff_idet_filter_line_c_16bit(const uint16_t *a
, const uint16_t *b
, const uint16_t *c
, int w
)
70 int v
= (*a
++ + *c
++) - 2 * *b
++;
77 static void filter(AVFilterContext
*ctx
)
79 IDETContext
*idet
= ctx
->priv
;
86 for (i
= 0; i
< idet
->csp
->nb_components
; i
++) {
87 int w
= idet
->cur
->width
;
88 int h
= idet
->cur
->height
;
89 int refs
= idet
->cur
->linesize
[i
];
92 w
= FF_CEIL_RSHIFT(w
, idet
->csp
->log2_chroma_w
);
93 h
= FF_CEIL_RSHIFT(h
, idet
->csp
->log2_chroma_h
);
96 for (y
= 2; y
< h
- 2; y
++) {
97 uint8_t *prev
= &idet
->prev
->data
[i
][y
*refs
];
98 uint8_t *cur
= &idet
->cur
->data
[i
][y
*refs
];
99 uint8_t *next
= &idet
->next
->data
[i
][y
*refs
];
100 alpha
[ y
&1] += idet
->filter_line(cur
-refs
, prev
, cur
+refs
, w
);
101 alpha
[(y
^1)&1] += idet
->filter_line(cur
-refs
, next
, cur
+refs
, w
);
102 delta
+= idet
->filter_line(cur
-refs
, cur
, cur
+refs
, w
);
106 if (alpha
[0] > idet
->interlace_threshold
* alpha
[1]){
108 }else if(alpha
[1] > idet
->interlace_threshold
* alpha
[0]){
110 }else if(alpha
[1] > idet
->progressive_threshold
* delta
){
116 memmove(idet
->history
+1, idet
->history
, HIST_SIZE
-1);
117 idet
->history
[0] = type
;
118 best_type
= UNDETERMINED
;
119 for(i
=0; i
<HIST_SIZE
; i
++){
120 if(idet
->history
[i
] != UNDETERMINED
){
121 if(best_type
== UNDETERMINED
)
122 best_type
= idet
->history
[i
];
124 if(idet
->history
[i
] == best_type
) {
132 if(idet
->last_type
== UNDETERMINED
){
133 if(match
) idet
->last_type
= best_type
;
135 if(match
>2) idet
->last_type
= best_type
;
138 if (idet
->last_type
== TFF
){
139 idet
->cur
->top_field_first
= 1;
140 idet
->cur
->interlaced_frame
= 1;
141 }else if(idet
->last_type
== BFF
){
142 idet
->cur
->top_field_first
= 0;
143 idet
->cur
->interlaced_frame
= 1;
144 }else if(idet
->last_type
== PROGRSSIVE
){
145 idet
->cur
->interlaced_frame
= 0;
148 idet
->prestat
[ type
] ++;
149 idet
->poststat
[idet
->last_type
] ++;
150 av_log(ctx
, AV_LOG_DEBUG
, "Single frame:%s, Multi frame:%s\n", type2str(type
), type2str(idet
->last_type
));
153 static int filter_frame(AVFilterLink
*link
, AVFrame
*picref
)
155 AVFilterContext
*ctx
= link
->dst
;
156 IDETContext
*idet
= ctx
->priv
;
159 av_frame_free(&idet
->prev
);
160 idet
->prev
= idet
->cur
;
161 idet
->cur
= idet
->next
;
168 idet
->prev
= av_frame_clone(idet
->cur
);
171 idet
->csp
= av_pix_fmt_desc_get(link
->format
);
172 if (idet
->csp
->comp
[0].depth_minus1
/ 8 == 1){
173 idet
->filter_line
= (ff_idet_filter_func
)ff_idet_filter_line_c_16bit
;
175 ff_idet_init_x86(idet
, 1);
180 return ff_filter_frame(ctx
->outputs
[0], av_frame_clone(idet
->cur
));
183 static av_cold
void uninit(AVFilterContext
*ctx
)
185 IDETContext
*idet
= ctx
->priv
;
187 av_log(ctx
, AV_LOG_INFO
, "Single frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
190 idet
->prestat
[PROGRSSIVE
],
191 idet
->prestat
[UNDETERMINED
]
193 av_log(ctx
, AV_LOG_INFO
, "Multi frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
196 idet
->poststat
[PROGRSSIVE
],
197 idet
->poststat
[UNDETERMINED
]
200 av_frame_free(&idet
->prev
);
201 av_frame_free(&idet
->cur
);
202 av_frame_free(&idet
->next
);
205 static int query_formats(AVFilterContext
*ctx
)
207 static const enum AVPixelFormat pix_fmts
[] = {
220 AV_PIX_FMT_YUV420P10
,
221 AV_PIX_FMT_YUV422P10
,
222 AV_PIX_FMT_YUV444P10
,
223 AV_PIX_FMT_YUV420P16
,
224 AV_PIX_FMT_YUV422P16
,
225 AV_PIX_FMT_YUV444P16
,
230 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
235 static int config_output(AVFilterLink
*outlink
)
237 outlink
->flags
|= FF_LINK_FLAG_REQUEST_LOOP
;
241 static av_cold
int init(AVFilterContext
*ctx
)
243 IDETContext
*idet
= ctx
->priv
;
245 idet
->last_type
= UNDETERMINED
;
246 memset(idet
->history
, UNDETERMINED
, HIST_SIZE
);
248 idet
->filter_line
= ff_idet_filter_line_c
;
251 ff_idet_init_x86(idet
, 0);
257 static const AVFilterPad idet_inputs
[] = {
260 .type
= AVMEDIA_TYPE_VIDEO
,
261 .filter_frame
= filter_frame
,
266 static const AVFilterPad idet_outputs
[] = {
269 .type
= AVMEDIA_TYPE_VIDEO
,
270 .config_props
= config_output
,
275 AVFilter ff_vf_idet
= {
277 .description
= NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
278 .priv_size
= sizeof(IDETContext
),
281 .query_formats
= query_formats
,
282 .inputs
= idet_inputs
,
283 .outputs
= idet_outputs
,
284 .priv_class
= &idet_class
,