]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_idet.c
vf_idet: use enum to represent the type.
[ffmpeg] / libavfilter / vf_idet.c
1 /*
2  * Copyright (C) 2012 Michael Niedermayer <michaelni@gmx.at>
3  *
4  * This file is part of FFmpeg.
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "libavutil/cpu.h"
22 #include "libavutil/common.h"
23 #include "libavutil/pixdesc.h"
24 #include "avfilter.h"
25
26 #undef NDEBUG
27 #include <assert.h>
28
29 typedef enum {
30     TFF,
31     BFF,
32     PROGRSSIVE,
33     UNDETERMINED,
34 } Type;
35
36 typedef struct {
37     float interlace_threshold;
38     float progressive_threshold;
39
40
41     Type prestat[4];
42
43     AVFilterBufferRef *cur;
44     AVFilterBufferRef *next;
45     AVFilterBufferRef *prev;
46     AVFilterBufferRef *out;
47     int (*filter_line)(uint8_t *prev, uint8_t *cur, uint8_t *next, int w);
48
49     const AVPixFmtDescriptor *csp;
50 } IDETContext;
51
52
53 static int filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
54 {
55     int x;
56     int ret=0;
57
58     for(x=0; x<w; x++){
59         ret += FFABS((*a++ + *c++) - 2 * *b++);
60     }
61
62     return ret;
63 }
64
65 static int filter_line_c_16bit(const uint16_t *a, const uint16_t *b, const uint16_t *c, int w)
66 {
67     int x;
68     int ret=0;
69
70     for(x=0; x<w; x++){
71         ret += FFABS((*a++ + *c++) - 2 * *b++);
72     }
73
74     return ret;
75 }
76
77 static void filter(AVFilterContext *ctx)
78 {
79     IDETContext *idet = ctx->priv;
80     int y, i;
81     int64_t alpha[2]={0};
82     int64_t delta=0;
83     Type type;
84
85     for (i = 0; i < idet->csp->nb_components; i++) {
86         int w = idet->cur->video->w;
87         int h = idet->cur->video->h;
88         int refs = idet->cur->linesize[i];
89         int df = (idet->csp->comp[i].depth_minus1 + 8) / 8;
90
91         if (i && i<3) {
92             w >>= idet->csp->log2_chroma_w;
93             h >>= idet->csp->log2_chroma_h;
94         }
95
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);
103         }
104     }
105 #if HAVE_MMX
106     __asm__ volatile("emms \n\t" : : : "memory");
107 #endif
108
109     if      (alpha[0] / (float)alpha[1] > idet->interlace_threshold){
110         type = TFF;
111     }else if(alpha[1] / (float)alpha[0] > idet->interlace_threshold){
112         type = BFF;
113     }else if(alpha[1] / (float)delta    > idet->progressive_threshold){
114         type = PROGRSSIVE;
115     }else{
116         type = UNDETERMINED;
117     }
118
119     idet->prestat[type] ++;
120
121     if      (type == TFF){
122         av_log(ctx, AV_LOG_INFO, "Interlaced, top field first\n");
123         idet->cur->video->top_field_first = 1;
124         idet->cur->video->interlaced = 1;
125     }else if(type == BFF){
126         av_log(ctx, AV_LOG_INFO, "Interlaced, bottom field first\n");
127         idet->cur->video->top_field_first = 0;
128         idet->cur->video->interlaced = 1;
129     }else if(type == PROGRSSIVE){
130         av_log(ctx, AV_LOG_INFO, "Progressive\n");
131         idet->cur->video->interlaced = 0;
132     }else{
133         av_log(ctx, AV_LOG_INFO, "Undetermined\n");
134         idet->cur->video->interlaced      = idet->prev->video->interlaced;
135         idet->cur->video->top_field_first = idet->prev->video->top_field_first;
136     }
137 }
138
139 static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
140 {
141     AVFilterContext *ctx = link->dst;
142     IDETContext *idet = ctx->priv;
143
144     if (idet->prev)
145         avfilter_unref_buffer(idet->prev);
146     idet->prev = idet->cur;
147     idet->cur  = idet->next;
148     idet->next = picref;
149
150     if (!idet->cur)
151         return;
152
153     if (!idet->prev)
154         idet->prev = avfilter_ref_buffer(idet->cur, AV_PERM_READ);
155
156     avfilter_start_frame(ctx->outputs[0], avfilter_ref_buffer(idet->cur, AV_PERM_READ));
157 }
158
159 static void end_frame(AVFilterLink *link)
160 {
161     AVFilterContext *ctx = link->dst;
162     IDETContext *idet = ctx->priv;
163
164     if (!idet->cur)
165         return;
166
167     if (!idet->csp)
168         idet->csp = &av_pix_fmt_descriptors[link->format];
169     if (idet->csp->comp[0].depth_minus1 / 8 == 1)
170         idet->filter_line = (void*)filter_line_c_16bit;
171
172     filter(ctx);
173
174     avfilter_draw_slice(ctx->outputs[0], 0, link->h, 1);
175     avfilter_end_frame(ctx->outputs[0]);
176 }
177
178 static int request_frame(AVFilterLink *link)
179 {
180     AVFilterContext *ctx = link->src;
181     IDETContext *idet = ctx->priv;
182
183     do {
184         int ret;
185
186         if ((ret = avfilter_request_frame(link->src->inputs[0])))
187             return ret;
188     } while (!idet->cur);
189
190     return 0;
191 }
192
193 static int poll_frame(AVFilterLink *link)
194 {
195     IDETContext *idet = link->src->priv;
196     int ret, val;
197
198     val = avfilter_poll_frame(link->src->inputs[0]);
199
200     if (val >= 1 && !idet->next) { //FIXME change API to not requre this red tape
201         if ((ret = avfilter_request_frame(link->src->inputs[0])) < 0)
202             return ret;
203         val = avfilter_poll_frame(link->src->inputs[0]);
204     }
205     assert(idet->next || !val);
206
207     return val;
208 }
209
210 static av_cold void uninit(AVFilterContext *ctx)
211 {
212     IDETContext *idet = ctx->priv;
213
214     av_log(ctx, AV_LOG_INFO, "TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
215            idet->prestat[TFF],
216            idet->prestat[BFF],
217            idet->prestat[PROGRSSIVE],
218            idet->prestat[UNDETERMINED]
219     );
220
221     if (idet->prev) avfilter_unref_buffer(idet->prev);
222     if (idet->cur ) avfilter_unref_buffer(idet->cur );
223     if (idet->next) avfilter_unref_buffer(idet->next);
224 }
225
226 static int query_formats(AVFilterContext *ctx)
227 {
228     static const enum PixelFormat pix_fmts[] = {
229         PIX_FMT_YUV420P,
230         PIX_FMT_YUV422P,
231         PIX_FMT_YUV444P,
232         PIX_FMT_YUV410P,
233         PIX_FMT_YUV411P,
234         PIX_FMT_GRAY8,
235         PIX_FMT_YUVJ420P,
236         PIX_FMT_YUVJ422P,
237         PIX_FMT_YUVJ444P,
238         AV_NE( PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE ),
239         PIX_FMT_YUV440P,
240         PIX_FMT_YUVJ440P,
241         AV_NE( PIX_FMT_YUV420P10BE, PIX_FMT_YUV420P10LE ),
242         AV_NE( PIX_FMT_YUV422P10BE, PIX_FMT_YUV422P10LE ),
243         AV_NE( PIX_FMT_YUV444P10BE, PIX_FMT_YUV444P10LE ),
244         AV_NE( PIX_FMT_YUV420P16BE, PIX_FMT_YUV420P16LE ),
245         AV_NE( PIX_FMT_YUV422P16BE, PIX_FMT_YUV422P16LE ),
246         AV_NE( PIX_FMT_YUV444P16BE, PIX_FMT_YUV444P16LE ),
247         PIX_FMT_YUVA420P,
248         PIX_FMT_NONE
249     };
250
251     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
252
253     return 0;
254 }
255
256 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
257 {
258     IDETContext *idet = ctx->priv;
259     int cpu_flags = av_get_cpu_flags();
260
261     idet->csp = NULL;
262
263     idet->interlace_threshold   = 1.01;
264     idet->progressive_threshold = 2.5;
265
266     if (args) sscanf(args, "%f:%f", &idet->interlace_threshold, &idet->progressive_threshold);
267
268     idet->filter_line = filter_line_c;
269
270     return 0;
271 }
272
273 static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
274
275 AVFilter avfilter_vf_idet = {
276     .name          = "idet",
277     .description   = NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
278
279     .priv_size     = sizeof(IDETContext),
280     .init          = init,
281     .uninit        = uninit,
282     .query_formats = query_formats,
283
284     .inputs    = (const AVFilterPad[]) {{ .name       = "default",
285                                           .type             = AVMEDIA_TYPE_VIDEO,
286                                           .start_frame      = start_frame,
287                                           .draw_slice       = null_draw_slice,
288                                           .end_frame        = end_frame,
289                                           .rej_perms        = AV_PERM_REUSE2, },
290                                         { .name = NULL}},
291
292     .outputs   = (const AVFilterPad[]) {{ .name       = "default",
293                                           .type             = AVMEDIA_TYPE_VIDEO,
294                                           .poll_frame       = poll_frame,
295                                           .request_frame    = request_frame, },
296                                         { .name = NULL}},
297 };