libavfilter/vf_tinterlace.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Stefano Sabatini
00003  * Copyright (c) 2010 Baptiste Coudurier
00004  * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com>
00005  *
00006  * This file is part of FFmpeg.
00007  *
00008  * FFmpeg is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * FFmpeg is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License along
00019  * with FFmpeg if not, write to the Free Software Foundation, Inc.,
00020  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00021  */
00022 
00028 #include "libavutil/imgutils.h"
00029 #include "avfilter.h"
00030 #include "internal.h"
00031 
00032 typedef struct {
00033     int mode;                   
00034     int frame;                  
00035     int vsub;                   
00036     AVFilterBufferRef *cur;
00037     AVFilterBufferRef *next;
00038     uint8_t *black_data[4];     
00039     int black_linesize[4];
00040 } TInterlaceContext;
00041 
00042 #define FULL_SCALE_YUVJ_FORMATS \
00043     PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P
00044 
00045 static enum PixelFormat full_scale_yuvj_pix_fmts[] = {
00046     FULL_SCALE_YUVJ_FORMATS, PIX_FMT_NONE
00047 };
00048 
00049 static int query_formats(AVFilterContext *ctx)
00050 {
00051     static const enum PixelFormat pix_fmts[] = {
00052         PIX_FMT_YUV420P,  PIX_FMT_YUV422P,  PIX_FMT_YUV444P,
00053         PIX_FMT_YUV444P,  PIX_FMT_YUV410P,  PIX_FMT_YUVA420P,
00054         PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS,
00055         PIX_FMT_NONE
00056     };
00057 
00058     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
00059     return 0;
00060 }
00061 
00062 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00063 {
00064     TInterlaceContext *tinterlace = ctx->priv;
00065     int n;
00066     tinterlace->mode = 0;
00067 
00068     if (args) {
00069         n = sscanf(args, "%d", &tinterlace->mode);
00070 
00071         if (n != 1 || tinterlace->mode < 0 || tinterlace->mode > 5) {
00072             av_log(ctx, AV_LOG_ERROR,
00073                    "Invalid mode '%s', use an integer between 0 and 5\n", args);
00074             return AVERROR(EINVAL);
00075         }
00076     }
00077 
00078     return 0;
00079 }
00080 
00081 static av_cold void uninit(AVFilterContext *ctx)
00082 {
00083     TInterlaceContext *tinterlace = ctx->priv;
00084 
00085     if (tinterlace->cur ) avfilter_unref_buffer(tinterlace->cur );
00086     if (tinterlace->next) avfilter_unref_buffer(tinterlace->next);
00087 
00088     av_freep(&tinterlace->black_data[0]);
00089 }
00090 
00091 static int config_out_props(AVFilterLink *outlink)
00092 {
00093     AVFilterContext *ctx = outlink->src;
00094     AVFilterLink *inlink = outlink->src->inputs[0];
00095     const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[outlink->format];
00096     TInterlaceContext *tinterlace = ctx->priv;
00097 
00098     tinterlace->vsub = desc->log2_chroma_h;
00099     outlink->w = inlink->w;
00100     outlink->h = tinterlace->mode == 0 || tinterlace->mode == 3 ?
00101         inlink->h*2 : inlink->h;
00102 
00103     if (tinterlace->mode == 3) {
00104         uint8_t black[4] = { 16, 128, 128, 16 };
00105         int i, ret;
00106         if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts))
00107             black[0] = black[3] = 0;
00108         ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize,
00109                              outlink->w, outlink->h, outlink->format, 1);
00110         if (ret < 0)
00111             return ret;
00112 
00113         /* fill black picture with black */
00114         for (i = 0; i < 4 && tinterlace->black_data[i]; i++) {
00115             int h = i == 1 || i == 2 ? outlink->h >> desc->log2_chroma_h : outlink->h;
00116             memset(tinterlace->black_data[i], black[i],
00117                    tinterlace->black_linesize[i] * h);
00118         }
00119     }
00120     av_log(ctx, AV_LOG_INFO, "mode:%d h:%d -> h:%d\n",
00121            tinterlace->mode, inlink->h, outlink->h);
00122 
00123     return 0;
00124 }
00125 
00126 #define FIELD_UPPER           0
00127 #define FIELD_LOWER           1
00128 #define FIELD_UPPER_AND_LOWER 2
00129 
00138 static inline
00139 void copy_picture_field(uint8_t *dst[4], int dst_linesize[4],
00140                         uint8_t *src[4], int src_linesize[4],
00141                         enum PixelFormat format, int w, int src_h,
00142                         int src_field, int interleave, int dst_field)
00143 {
00144     const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[format];
00145     int plane, vsub = desc->log2_chroma_h;
00146     int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2;
00147 
00148     for (plane = 0; plane < desc->nb_components; plane++) {
00149         int lines = plane == 1 || plane == 2 ? src_h >> vsub : src_h;
00150         int linesize = av_image_get_linesize(format, w, plane);
00151         uint8_t *dstp = dst[plane];
00152         uint8_t *srcp = src[plane];
00153         lines /= k;
00154         if (src_field == FIELD_LOWER)
00155             srcp += src_linesize[plane];
00156         if (interleave && dst_field == FIELD_LOWER)
00157             dstp += dst_linesize[plane];
00158         av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1),
00159                             srcp, src_linesize[plane]*k, linesize, lines);
00160     }
00161 }
00162 
00163 static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
00164 {
00165     AVFilterContext *ctx = inlink->dst;
00166     TInterlaceContext *tinterlace = ctx->priv;
00167 
00168     if (tinterlace->cur)
00169         avfilter_unref_buffer(tinterlace->cur);
00170     tinterlace->cur  = tinterlace->next;
00171     tinterlace->next = picref;
00172 }
00173 
00174 static void end_frame(AVFilterLink *inlink)
00175 {
00176     AVFilterContext *ctx = inlink->dst;
00177     AVFilterLink *outlink = ctx->outputs[0];
00178     TInterlaceContext *tinterlace = ctx->priv;
00179     AVFilterBufferRef *cur  = tinterlace->cur;
00180     AVFilterBufferRef *next = tinterlace->next;
00181     AVFilterBufferRef *out  = NULL;
00182     int field, tff;
00183 
00184     /* we need at least two frames */
00185     if (!tinterlace->cur)
00186         return;
00187 
00188     switch (tinterlace->mode) {
00189     case 0: /* move the odd frame into the upper field of the new image, even into
00190              * the lower field, generating a double-height video at half framerate */
00191         out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00192         avfilter_copy_buffer_ref_props(out, cur);
00193         out->video->h = outlink->h;
00194         out->video->interlaced = 1;
00195         out->video->top_field_first = 1;
00196 
00197         /* write odd frame lines into the upper field of the new frame */
00198         copy_picture_field(out->data, out->linesize,
00199                            cur->data, cur->linesize,
00200                            inlink->format, inlink->w, inlink->h,
00201                            FIELD_UPPER_AND_LOWER, 1, FIELD_UPPER);
00202         /* write even frame lines into the lower field of the new frame */
00203         copy_picture_field(out->data, out->linesize,
00204                            next->data, next->linesize,
00205                            inlink->format, inlink->w, inlink->h,
00206                            FIELD_UPPER_AND_LOWER, 1, FIELD_LOWER);
00207         avfilter_unref_buffer(tinterlace->next);
00208         tinterlace->next = NULL;
00209         break;
00210 
00211     case 1: /* only output even frames, odd  frames are dropped; height unchanged, half framerate */
00212     case 2: /* only output odd  frames, even frames are dropped; height unchanged, half framerate */
00213         out = avfilter_ref_buffer(tinterlace->mode == 2 ? cur : next, AV_PERM_READ);
00214         avfilter_unref_buffer(tinterlace->next);
00215         tinterlace->next = NULL;
00216         break;
00217 
00218     case 3: /* expand each frame to double height, but pad alternate
00219              * lines with black; framerate unchanged */
00220         out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00221         avfilter_copy_buffer_ref_props(out, cur);
00222         out->video->h = outlink->h;
00223 
00224         field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER;
00225         /* copy upper and lower fields */
00226         copy_picture_field(out->data, out->linesize,
00227                            cur->data, cur->linesize,
00228                            inlink->format, inlink->w, inlink->h,
00229                            FIELD_UPPER_AND_LOWER, 1, field);
00230         /* pad with black the other field */
00231         copy_picture_field(out->data, out->linesize,
00232                            tinterlace->black_data, tinterlace->black_linesize,
00233                            inlink->format, inlink->w, inlink->h,
00234                            FIELD_UPPER_AND_LOWER, 1, !field);
00235         break;
00236 
00237         /* interleave upper/lower lines from odd frames with lower/upper lines from even frames,
00238          * halving the frame rate and preserving image height */
00239     case 4: /* top    field first */
00240     case 5: /* bottom field first */
00241         tff = tinterlace->mode == 4;
00242         out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00243         avfilter_copy_buffer_ref_props(out, cur);
00244         out->video->interlaced = 1;
00245         out->video->top_field_first = tff;
00246 
00247         /* copy upper/lower field from cur */
00248         copy_picture_field(out->data, out->linesize,
00249                            cur->data, cur->linesize,
00250                            inlink->format, inlink->w, inlink->h,
00251                            tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER);
00252         /* copy lower/upper field from next */
00253         copy_picture_field(out->data, out->linesize,
00254                            next->data, next->linesize,
00255                            inlink->format, inlink->w, inlink->h,
00256                            tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER);
00257         avfilter_unref_buffer(tinterlace->next);
00258         tinterlace->next = NULL;
00259         break;
00260     }
00261 
00262     avfilter_start_frame(outlink, out);
00263     avfilter_draw_slice(outlink, 0, outlink->h, 1);
00264     avfilter_end_frame(outlink);
00265 
00266     tinterlace->frame++;
00267 }
00268 
00269 static int poll_frame(AVFilterLink *outlink)
00270 {
00271     TInterlaceContext *tinterlace = outlink->src->priv;
00272     AVFilterLink *inlink = outlink->src->inputs[0];
00273     int ret, val;
00274 
00275     val = avfilter_poll_frame(inlink);
00276 
00277     if (val == 1 && !tinterlace->next) {
00278         if ((ret = avfilter_request_frame(inlink)) < 0)
00279             return ret;
00280         val = avfilter_poll_frame(inlink);
00281     }
00282     assert(tinterlace->next);
00283 
00284     return val;
00285 }
00286 
00287 static int request_frame(AVFilterLink *outlink)
00288 {
00289     TInterlaceContext *tinterlace = outlink->src->priv;
00290     AVFilterLink *inlink = outlink->src->inputs[0];
00291 
00292     do {
00293         int ret;
00294 
00295         if ((ret = avfilter_request_frame(inlink)) < 0)
00296             return ret;
00297     } while (!tinterlace->cur);
00298 
00299     return 0;
00300 }
00301 
00302 static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
00303 
00304 AVFilter avfilter_vf_tinterlace = {
00305     .name          = "tinterlace",
00306     .description   = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
00307     .priv_size     = sizeof(TInterlaceContext),
00308     .init          = init,
00309     .uninit        = uninit,
00310     .query_formats = query_formats,
00311 
00312     .inputs = (const AVFilterPad[]) {
00313         { .name          = "default",
00314           .type          = AVMEDIA_TYPE_VIDEO,
00315           .start_frame   = start_frame,
00316           .draw_slice    = null_draw_slice,
00317           .end_frame     = end_frame, },
00318         { .name = NULL}
00319     },
00320     .outputs = (const AVFilterPad[]) {
00321         { .name          = "default",
00322           .type          = AVMEDIA_TYPE_VIDEO,
00323           .config_props  = config_out_props,
00324           .poll_frame    = poll_frame,
00325           .request_frame = request_frame },
00326         { .name = NULL}
00327     },
00328 };