]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_tinterlace.c
lavfi: do not pass opaque field to init functions
[ffmpeg] / libavfilter / vf_tinterlace.c
1 /*
2  * Copyright (c) 2011 Stefano Sabatini
3  * Copyright (c) 2010 Baptiste Coudurier
4  * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com>
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with FFmpeg if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 /**
24  * @file
25  * temporal field interlace filter, ported from MPlayer/libmpcodecs
26  */
27
28 #include "libavutil/imgutils.h"
29 #include "avfilter.h"
30 #include "internal.h"
31
32 enum TInterlaceMode {
33     MODE_MERGE = 0,
34     MODE_DROP_EVEN,
35     MODE_DROP_ODD,
36     MODE_PAD,
37     MODE_INTERLEAVE_TOP,
38     MODE_INTERLEAVE_BOTTOM,
39     MODE_INTERLACEX2,
40 };
41
42 static const char *tinterlace_mode_str[] = {
43     "merge",
44     "drop_even",
45     "drop_odd",
46     "pad",
47     "interleave_top",
48     "interleave_bottom",
49     "interlacex2",
50     NULL
51 };
52
53 typedef struct {
54     enum TInterlaceMode mode;   ///< interlace mode selected
55     int frame;                  ///< number of the output frame
56     int vsub;                   ///< chroma vertical subsampling
57     AVFilterBufferRef *cur;
58     AVFilterBufferRef *next;
59     uint8_t *black_data[4];     ///< buffer used to fill padded lines
60     int black_linesize[4];
61 } TInterlaceContext;
62
63 #define FULL_SCALE_YUVJ_FORMATS \
64     PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P
65
66 static enum PixelFormat full_scale_yuvj_pix_fmts[] = {
67     FULL_SCALE_YUVJ_FORMATS, PIX_FMT_NONE
68 };
69
70 static int query_formats(AVFilterContext *ctx)
71 {
72     static const enum PixelFormat pix_fmts[] = {
73         PIX_FMT_YUV420P,  PIX_FMT_YUV422P,  PIX_FMT_YUV444P,
74         PIX_FMT_YUV444P,  PIX_FMT_YUV410P,  PIX_FMT_YUVA420P,
75         PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS,
76         PIX_FMT_NONE
77     };
78
79     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
80     return 0;
81 }
82
83 static av_cold int init(AVFilterContext *ctx, const char *args)
84 {
85     TInterlaceContext *tinterlace = ctx->priv;
86     int i;
87     char c;
88
89     tinterlace->mode = MODE_MERGE;
90
91     if (args) {
92         if (sscanf(args, "%d%c", (int *)&tinterlace->mode, &c) == 1) {
93             if (tinterlace->mode > 6) {
94                 av_log(ctx, AV_LOG_ERROR,
95                        "Invalid mode '%s', use an integer between 0 and 6\n", args);
96                 return AVERROR(EINVAL);
97             }
98
99             av_log(ctx, AV_LOG_WARNING,
100                    "Using numeric constant is deprecated, use symbolic values\n");
101         } else {
102             for (i = 0; tinterlace_mode_str[i]; i++) {
103                 if (!strcmp(tinterlace_mode_str[i], args)) {
104                     tinterlace->mode = i;
105                     break;
106                 }
107             }
108             if (!tinterlace_mode_str[i]) {
109                 av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s'\n", args);
110                 return AVERROR(EINVAL);
111             }
112         }
113     }
114
115     return 0;
116 }
117
118 static av_cold void uninit(AVFilterContext *ctx)
119 {
120     TInterlaceContext *tinterlace = ctx->priv;
121
122     if (tinterlace->cur ) avfilter_unref_bufferp(&tinterlace->cur );
123     if (tinterlace->next) avfilter_unref_bufferp(&tinterlace->next);
124
125     av_freep(&tinterlace->black_data[0]);
126 }
127
128 static int config_out_props(AVFilterLink *outlink)
129 {
130     AVFilterContext *ctx = outlink->src;
131     AVFilterLink *inlink = outlink->src->inputs[0];
132     const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[outlink->format];
133     TInterlaceContext *tinterlace = ctx->priv;
134
135     tinterlace->vsub = desc->log2_chroma_h;
136     outlink->w = inlink->w;
137     outlink->h = tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD ?
138         inlink->h*2 : inlink->h;
139
140     if (tinterlace->mode == MODE_PAD) {
141         uint8_t black[4] = { 16, 128, 128, 16 };
142         int i, ret;
143         if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts))
144             black[0] = black[3] = 0;
145         ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize,
146                              outlink->w, outlink->h, outlink->format, 1);
147         if (ret < 0)
148             return ret;
149
150         /* fill black picture with black */
151         for (i = 0; i < 4 && tinterlace->black_data[i]; i++) {
152             int h = i == 1 || i == 2 ? outlink->h >> desc->log2_chroma_h : outlink->h;
153             memset(tinterlace->black_data[i], black[i],
154                    tinterlace->black_linesize[i] * h);
155         }
156     }
157     av_log(ctx, AV_LOG_INFO, "mode:%s h:%d -> h:%d\n",
158            tinterlace_mode_str[tinterlace->mode], inlink->h, outlink->h);
159
160     return 0;
161 }
162
163 #define FIELD_UPPER           0
164 #define FIELD_LOWER           1
165 #define FIELD_UPPER_AND_LOWER 2
166
167 /**
168  * Copy picture field from src to dst.
169  *
170  * @param src_field copy from upper, lower field or both
171  * @param interleave leave a padding line between each copied line
172  * @param dst_field copy to upper or lower field,
173  *        only meaningful when interleave is selected
174  */
175 static inline
176 void copy_picture_field(uint8_t *dst[4], int dst_linesize[4],
177                         uint8_t *src[4], int src_linesize[4],
178                         enum PixelFormat format, int w, int src_h,
179                         int src_field, int interleave, int dst_field)
180 {
181     const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[format];
182     int plane, vsub = desc->log2_chroma_h;
183     int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2;
184
185     for (plane = 0; plane < desc->nb_components; plane++) {
186         int lines = plane == 1 || plane == 2 ? src_h >> vsub : src_h;
187         int linesize = av_image_get_linesize(format, w, plane);
188         uint8_t *dstp = dst[plane];
189         uint8_t *srcp = src[plane];
190         lines /= k;
191         if (src_field == FIELD_LOWER)
192             srcp += src_linesize[plane];
193         if (interleave && dst_field == FIELD_LOWER)
194             dstp += dst_linesize[plane];
195         av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1),
196                             srcp, src_linesize[plane]*k, linesize, lines);
197     }
198 }
199
200 static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
201 {
202     AVFilterContext *ctx = inlink->dst;
203     TInterlaceContext *tinterlace = ctx->priv;
204
205     avfilter_unref_buffer(tinterlace->cur);
206     tinterlace->cur  = tinterlace->next;
207     tinterlace->next = picref;
208 }
209
210 static void end_frame(AVFilterLink *inlink)
211 {
212     AVFilterContext *ctx = inlink->dst;
213     AVFilterLink *outlink = ctx->outputs[0];
214     TInterlaceContext *tinterlace = ctx->priv;
215     AVFilterBufferRef *cur  = tinterlace->cur;
216     AVFilterBufferRef *next = tinterlace->next;
217     AVFilterBufferRef *out  = NULL;
218     int field, tff;
219
220     /* we need at least two frames */
221     if (!tinterlace->cur)
222         return;
223
224     switch (tinterlace->mode) {
225     case MODE_MERGE: /* move the odd frame into the upper field of the new image, even into
226              * the lower field, generating a double-height video at half framerate */
227         out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
228         avfilter_copy_buffer_ref_props(out, cur);
229         out->video->h = outlink->h;
230         out->video->interlaced = 1;
231         out->video->top_field_first = 1;
232
233         /* write odd frame lines into the upper field of the new frame */
234         copy_picture_field(out->data, out->linesize,
235                            cur->data, cur->linesize,
236                            inlink->format, inlink->w, inlink->h,
237                            FIELD_UPPER_AND_LOWER, 1, FIELD_UPPER);
238         /* write even frame lines into the lower field of the new frame */
239         copy_picture_field(out->data, out->linesize,
240                            next->data, next->linesize,
241                            inlink->format, inlink->w, inlink->h,
242                            FIELD_UPPER_AND_LOWER, 1, FIELD_LOWER);
243         avfilter_unref_bufferp(&tinterlace->next);
244         break;
245
246     case MODE_DROP_ODD:  /* only output even frames, odd  frames are dropped; height unchanged, half framerate */
247     case MODE_DROP_EVEN: /* only output odd  frames, even frames are dropped; height unchanged, half framerate */
248         out = avfilter_ref_buffer(tinterlace->mode == MODE_DROP_EVEN ? cur : next, AV_PERM_READ);
249         avfilter_unref_bufferp(&tinterlace->next);
250         break;
251
252     case MODE_PAD: /* expand each frame to double height, but pad alternate
253                     * lines with black; framerate unchanged */
254         out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
255         avfilter_copy_buffer_ref_props(out, cur);
256         out->video->h = outlink->h;
257
258         field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER;
259         /* copy upper and lower fields */
260         copy_picture_field(out->data, out->linesize,
261                            cur->data, cur->linesize,
262                            inlink->format, inlink->w, inlink->h,
263                            FIELD_UPPER_AND_LOWER, 1, field);
264         /* pad with black the other field */
265         copy_picture_field(out->data, out->linesize,
266                            tinterlace->black_data, tinterlace->black_linesize,
267                            inlink->format, inlink->w, inlink->h,
268                            FIELD_UPPER_AND_LOWER, 1, !field);
269         break;
270
271         /* interleave upper/lower lines from odd frames with lower/upper lines from even frames,
272          * halving the frame rate and preserving image height */
273     case MODE_INTERLEAVE_TOP:    /* top    field first */
274     case MODE_INTERLEAVE_BOTTOM: /* bottom field first */
275         tff = tinterlace->mode == MODE_INTERLEAVE_TOP;
276         out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
277         avfilter_copy_buffer_ref_props(out, cur);
278         out->video->interlaced = 1;
279         out->video->top_field_first = tff;
280
281         /* copy upper/lower field from cur */
282         copy_picture_field(out->data, out->linesize,
283                            cur->data, cur->linesize,
284                            inlink->format, inlink->w, inlink->h,
285                            tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER);
286         /* copy lower/upper field from next */
287         copy_picture_field(out->data, out->linesize,
288                            next->data, next->linesize,
289                            inlink->format, inlink->w, inlink->h,
290                            tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER);
291         avfilter_unref_bufferp(&tinterlace->next);
292         break;
293     case MODE_INTERLACEX2: /* re-interlace preserving image height, double frame rate */
294         /* output current frame first */
295         out = avfilter_ref_buffer(cur, AV_PERM_READ);
296         out->video->interlaced = 1;
297
298         ff_start_frame(outlink, out);
299         ff_draw_slice(outlink, 0, outlink->h, 1);
300         ff_end_frame(outlink);
301
302         /* output mix of current and next frame */
303         tff = next->video->top_field_first;
304         out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
305         avfilter_copy_buffer_ref_props(out, next);
306         out->video->interlaced = 1;
307
308         /* write current frame second field lines into the second field of the new frame */
309         copy_picture_field(out->data, out->linesize,
310                            cur->data, cur->linesize,
311                            inlink->format, inlink->w, inlink->h,
312                            tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER);
313         /* write next frame first field lines into the first field of the new frame */
314         copy_picture_field(out->data, out->linesize,
315                            next->data, next->linesize,
316                            inlink->format, inlink->w, inlink->h,
317                            tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER);
318         break;
319     }
320
321     ff_start_frame(outlink, out);
322     ff_draw_slice(outlink, 0, outlink->h, 1);
323     ff_end_frame(outlink);
324
325     tinterlace->frame++;
326 }
327
328 static int poll_frame(AVFilterLink *outlink)
329 {
330     TInterlaceContext *tinterlace = outlink->src->priv;
331     AVFilterLink *inlink = outlink->src->inputs[0];
332     int ret, val;
333
334     val = ff_poll_frame(inlink);
335
336     if (val == 1 && !tinterlace->next) {
337         if ((ret = ff_request_frame(inlink)) < 0)
338             return ret;
339         val = ff_poll_frame(inlink);
340     }
341     assert(tinterlace->next);
342
343     return val;
344 }
345
346 static int request_frame(AVFilterLink *outlink)
347 {
348     TInterlaceContext *tinterlace = outlink->src->priv;
349     AVFilterLink *inlink = outlink->src->inputs[0];
350
351     do {
352         int ret;
353
354         if ((ret = ff_request_frame(inlink)) < 0)
355             return ret;
356     } while (!tinterlace->cur);
357
358     return 0;
359 }
360
361 static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
362
363 AVFilter avfilter_vf_tinterlace = {
364     .name          = "tinterlace",
365     .description   = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
366     .priv_size     = sizeof(TInterlaceContext),
367     .init          = init,
368     .uninit        = uninit,
369     .query_formats = query_formats,
370
371     .inputs = (const AVFilterPad[]) {
372         { .name          = "default",
373           .type          = AVMEDIA_TYPE_VIDEO,
374           .start_frame   = start_frame,
375           .draw_slice    = null_draw_slice,
376           .end_frame     = end_frame, },
377         { .name = NULL}
378     },
379     .outputs = (const AVFilterPad[]) {
380         { .name          = "default",
381           .type          = AVMEDIA_TYPE_VIDEO,
382           .config_props  = config_out_props,
383           .poll_frame    = poll_frame,
384           .request_frame = request_frame },
385         { .name = NULL}
386     },
387 };