]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_idet.c
Merge remote-tracking branch 'qatar/master'
[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 #define HIST_SIZE 4
30
31 typedef enum {
32     TFF,
33     BFF,
34     PROGRSSIVE,
35     UNDETERMINED,
36 } Type;
37
38 typedef struct {
39     float interlace_threshold;
40     float progressive_threshold;
41
42     Type last_type;
43     Type prestat[4];
44     Type poststat[4];
45
46     uint8_t history[HIST_SIZE];
47
48     AVFilterBufferRef *cur;
49     AVFilterBufferRef *next;
50     AVFilterBufferRef *prev;
51     AVFilterBufferRef *out;
52     int (*filter_line)(uint8_t *prev, uint8_t *cur, uint8_t *next, int w);
53
54     const AVPixFmtDescriptor *csp;
55 } IDETContext;
56
57 static const char *type2str(Type type)
58 {
59     switch(type) {
60         case TFF         : return "Top Field First   ";
61         case BFF         : return "Bottom Field First";
62         case PROGRSSIVE  : return "Progressive       ";
63         case UNDETERMINED: return "Undetermined      ";
64     }
65     return NULL;
66 }
67
68 static int filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
69 {
70     int x;
71     int ret=0;
72
73     for(x=0; x<w; x++){
74         ret += FFABS((*a++ + *c++) - 2 * *b++);
75     }
76
77     return ret;
78 }
79
80 static int filter_line_c_16bit(const uint16_t *a, const uint16_t *b, const uint16_t *c, int w)
81 {
82     int x;
83     int ret=0;
84
85     for(x=0; x<w; x++){
86         ret += FFABS((*a++ + *c++) - 2 * *b++);
87     }
88
89     return ret;
90 }
91
92 static void filter(AVFilterContext *ctx)
93 {
94     IDETContext *idet = ctx->priv;
95     int y, i;
96     int64_t alpha[2]={0};
97     int64_t delta=0;
98     Type type, best_type;
99     int match = 0;
100
101     for (i = 0; i < idet->csp->nb_components; i++) {
102         int w = idet->cur->video->w;
103         int h = idet->cur->video->h;
104         int refs = idet->cur->linesize[i];
105         int df = (idet->csp->comp[i].depth_minus1 + 8) / 8;
106
107         if (i && i<3) {
108             w >>= idet->csp->log2_chroma_w;
109             h >>= idet->csp->log2_chroma_h;
110         }
111
112         for (y = 2; y < h - 2; y++) {
113             uint8_t *prev = &idet->prev->data[i][y*refs];
114             uint8_t *cur  = &idet->cur ->data[i][y*refs];
115             uint8_t *next = &idet->next->data[i][y*refs];
116             alpha[ y   &1] += idet->filter_line(cur-refs, prev, cur+refs, w);
117             alpha[(y^1)&1] += idet->filter_line(cur-refs, next, cur+refs, w);
118             delta          += idet->filter_line(cur-refs,  cur, cur+refs, w);
119         }
120     }
121 #if HAVE_MMX
122     __asm__ volatile("emms \n\t" : : : "memory");
123 #endif
124
125     if      (alpha[0] / (float)alpha[1] > idet->interlace_threshold){
126         type = TFF;
127     }else if(alpha[1] / (float)alpha[0] > idet->interlace_threshold){
128         type = BFF;
129     }else if(alpha[1] / (float)delta    > idet->progressive_threshold){
130         type = PROGRSSIVE;
131     }else{
132         type = UNDETERMINED;
133     }
134
135     memmove(idet->history+1, idet->history, HIST_SIZE-1);
136     idet->history[0] = type;
137     best_type = UNDETERMINED;
138     for(i=0; i<HIST_SIZE; i++){
139         if(idet->history[i] != UNDETERMINED){
140             if(best_type == UNDETERMINED)
141                 best_type = idet->history[i];
142
143             if(idet->history[i] == best_type) {
144                 match++;
145             }else{
146                 match=0;
147                 break;
148             }
149         }
150     }
151     if(idet->last_type == UNDETERMINED){
152         if(match  ) idet->last_type = best_type;
153     }else{
154         if(match>2) idet->last_type = best_type;
155     }
156
157     if      (idet->last_type == TFF){
158         idet->cur->video->top_field_first = 1;
159         idet->cur->video->interlaced = 1;
160     }else if(idet->last_type == BFF){
161         idet->cur->video->top_field_first = 0;
162         idet->cur->video->interlaced = 1;
163     }else if(idet->last_type == PROGRSSIVE){
164         idet->cur->video->interlaced = 0;
165     }
166
167     idet->prestat [           type] ++;
168     idet->poststat[idet->last_type] ++;
169     av_log(ctx, AV_LOG_DEBUG, "Single frame:%s, Multi frame:%s\n", type2str(type), type2str(idet->last_type));
170 }
171
172 static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
173 {
174     AVFilterContext *ctx = link->dst;
175     IDETContext *idet = ctx->priv;
176
177     if (idet->prev)
178         avfilter_unref_buffer(idet->prev);
179     idet->prev = idet->cur;
180     idet->cur  = idet->next;
181     idet->next = picref;
182
183     if (!idet->cur)
184         return;
185
186     if (!idet->prev)
187         idet->prev = avfilter_ref_buffer(idet->cur, AV_PERM_READ);
188
189     avfilter_start_frame(ctx->outputs[0], avfilter_ref_buffer(idet->cur, AV_PERM_READ));
190 }
191
192 static void end_frame(AVFilterLink *link)
193 {
194     AVFilterContext *ctx = link->dst;
195     IDETContext *idet = ctx->priv;
196
197     if (!idet->cur)
198         return;
199
200     if (!idet->csp)
201         idet->csp = &av_pix_fmt_descriptors[link->format];
202     if (idet->csp->comp[0].depth_minus1 / 8 == 1)
203         idet->filter_line = (void*)filter_line_c_16bit;
204
205     filter(ctx);
206
207     avfilter_draw_slice(ctx->outputs[0], 0, link->h, 1);
208     avfilter_end_frame(ctx->outputs[0]);
209 }
210
211 static int request_frame(AVFilterLink *link)
212 {
213     AVFilterContext *ctx = link->src;
214     IDETContext *idet = ctx->priv;
215
216     do {
217         int ret;
218
219         if ((ret = avfilter_request_frame(link->src->inputs[0])))
220             return ret;
221     } while (!idet->cur);
222
223     return 0;
224 }
225
226 static int poll_frame(AVFilterLink *link)
227 {
228     IDETContext *idet = link->src->priv;
229     int ret, val;
230
231     val = avfilter_poll_frame(link->src->inputs[0]);
232
233     if (val >= 1 && !idet->next) { //FIXME change API to not requre this red tape
234         if ((ret = avfilter_request_frame(link->src->inputs[0])) < 0)
235             return ret;
236         val = avfilter_poll_frame(link->src->inputs[0]);
237     }
238     assert(idet->next || !val);
239
240     return val;
241 }
242
243 static av_cold void uninit(AVFilterContext *ctx)
244 {
245     IDETContext *idet = ctx->priv;
246
247     av_log(ctx, AV_LOG_INFO, "Single frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
248            idet->prestat[TFF],
249            idet->prestat[BFF],
250            idet->prestat[PROGRSSIVE],
251            idet->prestat[UNDETERMINED]
252     );
253     av_log(ctx, AV_LOG_INFO, "Multi frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
254            idet->poststat[TFF],
255            idet->poststat[BFF],
256            idet->poststat[PROGRSSIVE],
257            idet->poststat[UNDETERMINED]
258     );
259
260     if (idet->prev) avfilter_unref_buffer(idet->prev);
261     if (idet->cur ) avfilter_unref_buffer(idet->cur );
262     if (idet->next) avfilter_unref_buffer(idet->next);
263 }
264
265 static int query_formats(AVFilterContext *ctx)
266 {
267     static const enum PixelFormat pix_fmts[] = {
268         PIX_FMT_YUV420P,
269         PIX_FMT_YUV422P,
270         PIX_FMT_YUV444P,
271         PIX_FMT_YUV410P,
272         PIX_FMT_YUV411P,
273         PIX_FMT_GRAY8,
274         PIX_FMT_YUVJ420P,
275         PIX_FMT_YUVJ422P,
276         PIX_FMT_YUVJ444P,
277         AV_NE( PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE ),
278         PIX_FMT_YUV440P,
279         PIX_FMT_YUVJ440P,
280         AV_NE( PIX_FMT_YUV420P10BE, PIX_FMT_YUV420P10LE ),
281         AV_NE( PIX_FMT_YUV422P10BE, PIX_FMT_YUV422P10LE ),
282         AV_NE( PIX_FMT_YUV444P10BE, PIX_FMT_YUV444P10LE ),
283         AV_NE( PIX_FMT_YUV420P16BE, PIX_FMT_YUV420P16LE ),
284         AV_NE( PIX_FMT_YUV422P16BE, PIX_FMT_YUV422P16LE ),
285         AV_NE( PIX_FMT_YUV444P16BE, PIX_FMT_YUV444P16LE ),
286         PIX_FMT_YUVA420P,
287         PIX_FMT_NONE
288     };
289
290     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
291
292     return 0;
293 }
294
295 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
296 {
297     IDETContext *idet = ctx->priv;
298     int cpu_flags = av_get_cpu_flags();
299
300     idet->csp = NULL;
301
302     idet->interlace_threshold   = 1.01;
303     idet->progressive_threshold = 2.5;
304
305     if (args) sscanf(args, "%f:%f", &idet->interlace_threshold, &idet->progressive_threshold);
306
307     idet->last_type = UNDETERMINED;
308     memset(idet->history, UNDETERMINED, HIST_SIZE);
309
310     idet->filter_line = filter_line_c;
311
312     return 0;
313 }
314
315 static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
316
317 AVFilter avfilter_vf_idet = {
318     .name          = "idet",
319     .description   = NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
320
321     .priv_size     = sizeof(IDETContext),
322     .init          = init,
323     .uninit        = uninit,
324     .query_formats = query_formats,
325
326     .inputs    = (const AVFilterPad[]) {{ .name       = "default",
327                                           .type             = AVMEDIA_TYPE_VIDEO,
328                                           .start_frame      = start_frame,
329                                           .draw_slice       = null_draw_slice,
330                                           .end_frame        = end_frame,
331                                           .rej_perms        = AV_PERM_REUSE2, },
332                                         { .name = NULL}},
333
334     .outputs   = (const AVFilterPad[]) {{ .name       = "default",
335                                           .type             = AVMEDIA_TYPE_VIDEO,
336                                           .poll_frame       = poll_frame,
337                                           .request_frame    = request_frame, },
338                                         { .name = NULL}},
339 };