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