00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00028 #include "libavutil/opt.h"
00029 #include "libavutil/imgutils.h"
00030 #include "libavutil/avassert.h"
00031 #include "avfilter.h"
00032 #include "internal.h"
00033
00034 enum TInterlaceMode {
00035 MODE_MERGE = 0,
00036 MODE_DROP_EVEN,
00037 MODE_DROP_ODD,
00038 MODE_PAD,
00039 MODE_INTERLEAVE_TOP,
00040 MODE_INTERLEAVE_BOTTOM,
00041 MODE_INTERLACEX2,
00042 MODE_NB,
00043 };
00044
00045 typedef struct {
00046 const AVClass *class;
00047 enum TInterlaceMode mode;
00048 int frame;
00049 int vsub;
00050 AVFilterBufferRef *cur;
00051 AVFilterBufferRef *next;
00052 uint8_t *black_data[4];
00053 int black_linesize[4];
00054 } TInterlaceContext;
00055
00056 #define OFFSET(x) offsetof(TInterlaceContext, x)
00057 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
00058
00059 static const AVOption tinterlace_options[] = {
00060 {"mode", "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, "mode"},
00061 {"merge", "merge fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE}, INT_MIN, INT_MAX, FLAGS, "mode"},
00062 {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN}, INT_MIN, INT_MAX, FLAGS, "mode"},
00063 {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD}, INT_MIN, INT_MAX, FLAGS, "mode"},
00064 {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD}, INT_MIN, INT_MAX, FLAGS, "mode"},
00065 {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP}, INT_MIN, INT_MAX, FLAGS, "mode"},
00066 {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "mode"},
00067 {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2}, INT_MIN, INT_MAX, FLAGS, "mode"},
00068
00069 {NULL}
00070 };
00071
00072 AVFILTER_DEFINE_CLASS(tinterlace);
00073
00074 #define FULL_SCALE_YUVJ_FORMATS \
00075 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P
00076
00077 static enum AVPixelFormat full_scale_yuvj_pix_fmts[] = {
00078 FULL_SCALE_YUVJ_FORMATS, AV_PIX_FMT_NONE
00079 };
00080
00081 static int query_formats(AVFilterContext *ctx)
00082 {
00083 static const enum AVPixelFormat pix_fmts[] = {
00084 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
00085 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUVA420P,
00086 AV_PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS,
00087 AV_PIX_FMT_NONE
00088 };
00089
00090 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00091 return 0;
00092 }
00093
00094 static av_cold int init(AVFilterContext *ctx, const char *args)
00095 {
00096 TInterlaceContext *tinterlace = ctx->priv;
00097 static const char *shorthand[] = { "mode", NULL };
00098
00099 tinterlace->class = &tinterlace_class;
00100 av_opt_set_defaults(tinterlace);
00101
00102 return av_opt_set_from_string(tinterlace, args, shorthand, "=", ":");
00103 }
00104
00105 static av_cold void uninit(AVFilterContext *ctx)
00106 {
00107 TInterlaceContext *tinterlace = ctx->priv;
00108
00109 avfilter_unref_bufferp(&tinterlace->cur );
00110 avfilter_unref_bufferp(&tinterlace->next);
00111
00112 av_opt_free(tinterlace);
00113 av_freep(&tinterlace->black_data[0]);
00114 }
00115
00116 static int config_out_props(AVFilterLink *outlink)
00117 {
00118 AVFilterContext *ctx = outlink->src;
00119 AVFilterLink *inlink = outlink->src->inputs[0];
00120 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
00121 TInterlaceContext *tinterlace = ctx->priv;
00122
00123 tinterlace->vsub = desc->log2_chroma_h;
00124 outlink->w = inlink->w;
00125 outlink->h = tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD ?
00126 inlink->h*2 : inlink->h;
00127
00128 if (tinterlace->mode == MODE_PAD) {
00129 uint8_t black[4] = { 16, 128, 128, 16 };
00130 int i, ret;
00131 if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts))
00132 black[0] = black[3] = 0;
00133 ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize,
00134 outlink->w, outlink->h, outlink->format, 1);
00135 if (ret < 0)
00136 return ret;
00137
00138
00139 for (i = 0; i < 4 && tinterlace->black_data[i]; i++) {
00140 int h = i == 1 || i == 2 ? outlink->h >> desc->log2_chroma_h : outlink->h;
00141 memset(tinterlace->black_data[i], black[i],
00142 tinterlace->black_linesize[i] * h);
00143 }
00144 }
00145 av_log(ctx, AV_LOG_VERBOSE, "mode:%d h:%d -> h:%d\n",
00146 tinterlace->mode, inlink->h, outlink->h);
00147
00148 return 0;
00149 }
00150
00151 #define FIELD_UPPER 0
00152 #define FIELD_LOWER 1
00153 #define FIELD_UPPER_AND_LOWER 2
00154
00163 static inline
00164 void copy_picture_field(uint8_t *dst[4], int dst_linesize[4],
00165 const uint8_t *src[4], int src_linesize[4],
00166 enum AVPixelFormat format, int w, int src_h,
00167 int src_field, int interleave, int dst_field)
00168 {
00169 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format);
00170 int plane, vsub = desc->log2_chroma_h;
00171 int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2;
00172
00173 for (plane = 0; plane < desc->nb_components; plane++) {
00174 int lines = plane == 1 || plane == 2 ? src_h >> vsub : src_h;
00175 int linesize = av_image_get_linesize(format, w, plane);
00176 uint8_t *dstp = dst[plane];
00177 const uint8_t *srcp = src[plane];
00178
00179 if (linesize < 0)
00180 return;
00181
00182 lines /= k;
00183 if (src_field == FIELD_LOWER)
00184 srcp += src_linesize[plane];
00185 if (interleave && dst_field == FIELD_LOWER)
00186 dstp += dst_linesize[plane];
00187 av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1),
00188 srcp, src_linesize[plane]*k, linesize, lines);
00189 }
00190 }
00191
00192 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
00193 {
00194 AVFilterContext *ctx = inlink->dst;
00195 AVFilterLink *outlink = ctx->outputs[0];
00196 TInterlaceContext *tinterlace = ctx->priv;
00197 AVFilterBufferRef *cur, *next, *out;
00198 int field, tff, ret;
00199
00200 avfilter_unref_buffer(tinterlace->cur);
00201 tinterlace->cur = tinterlace->next;
00202 tinterlace->next = picref;
00203
00204 cur = tinterlace->cur;
00205 next = tinterlace->next;
00206
00207 if (!tinterlace->cur)
00208 return 0;
00209
00210 switch (tinterlace->mode) {
00211 case MODE_MERGE:
00212
00213 out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00214 if (!out)
00215 return AVERROR(ENOMEM);
00216 avfilter_copy_buffer_ref_props(out, cur);
00217 out->video->h = outlink->h;
00218 out->video->interlaced = 1;
00219 out->video->top_field_first = 1;
00220
00221
00222 copy_picture_field(out->data, out->linesize,
00223 (const uint8_t **)cur->data, cur->linesize,
00224 inlink->format, inlink->w, inlink->h,
00225 FIELD_UPPER_AND_LOWER, 1, FIELD_UPPER);
00226
00227 copy_picture_field(out->data, out->linesize,
00228 (const uint8_t **)next->data, next->linesize,
00229 inlink->format, inlink->w, inlink->h,
00230 FIELD_UPPER_AND_LOWER, 1, FIELD_LOWER);
00231 avfilter_unref_bufferp(&tinterlace->next);
00232 break;
00233
00234 case MODE_DROP_ODD:
00235 case MODE_DROP_EVEN:
00236 out = avfilter_ref_buffer(tinterlace->mode == MODE_DROP_EVEN ? cur : next, AV_PERM_READ);
00237 avfilter_unref_bufferp(&tinterlace->next);
00238 break;
00239
00240 case MODE_PAD:
00241
00242 out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00243 avfilter_copy_buffer_ref_props(out, cur);
00244 out->video->h = outlink->h;
00245
00246 field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER;
00247
00248 copy_picture_field(out->data, out->linesize,
00249 (const uint8_t **)cur->data, cur->linesize,
00250 inlink->format, inlink->w, inlink->h,
00251 FIELD_UPPER_AND_LOWER, 1, field);
00252
00253 copy_picture_field(out->data, out->linesize,
00254 (const uint8_t **)tinterlace->black_data, tinterlace->black_linesize,
00255 inlink->format, inlink->w, inlink->h,
00256 FIELD_UPPER_AND_LOWER, 1, !field);
00257 break;
00258
00259
00260
00261 case MODE_INTERLEAVE_TOP:
00262 case MODE_INTERLEAVE_BOTTOM:
00263 tff = tinterlace->mode == MODE_INTERLEAVE_TOP;
00264 out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00265 if (!out)
00266 return AVERROR(ENOMEM);
00267 avfilter_copy_buffer_ref_props(out, cur);
00268 out->video->interlaced = 1;
00269 out->video->top_field_first = tff;
00270
00271
00272 copy_picture_field(out->data, out->linesize,
00273 (const uint8_t **)cur->data, cur->linesize,
00274 inlink->format, inlink->w, inlink->h,
00275 tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER);
00276
00277 copy_picture_field(out->data, out->linesize,
00278 (const uint8_t **)next->data, next->linesize,
00279 inlink->format, inlink->w, inlink->h,
00280 tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER);
00281 avfilter_unref_bufferp(&tinterlace->next);
00282 break;
00283 case MODE_INTERLACEX2:
00284
00285 out = avfilter_ref_buffer(cur, ~AV_PERM_WRITE);
00286 if (!out)
00287 return AVERROR(ENOMEM);
00288 out->video->interlaced = 1;
00289
00290 if ((ret = ff_filter_frame(outlink, out)) < 0)
00291 return ret;
00292
00293
00294 tff = next->video->top_field_first;
00295 out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00296 if (!out)
00297 return AVERROR(ENOMEM);
00298 avfilter_copy_buffer_ref_props(out, next);
00299 out->video->interlaced = 1;
00300
00301
00302 copy_picture_field(out->data, out->linesize,
00303 (const uint8_t **)cur->data, cur->linesize,
00304 inlink->format, inlink->w, inlink->h,
00305 tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER);
00306
00307 copy_picture_field(out->data, out->linesize,
00308 (const uint8_t **)next->data, next->linesize,
00309 inlink->format, inlink->w, inlink->h,
00310 tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER);
00311 break;
00312 default:
00313 av_assert0(0);
00314 }
00315
00316 ret = ff_filter_frame(outlink, out);
00317 tinterlace->frame++;
00318
00319 return ret;
00320 }
00321
00322 static int request_frame(AVFilterLink *outlink)
00323 {
00324 TInterlaceContext *tinterlace = outlink->src->priv;
00325 AVFilterLink *inlink = outlink->src->inputs[0];
00326
00327 do {
00328 int ret;
00329
00330 if ((ret = ff_request_frame(inlink)) < 0)
00331 return ret;
00332 } while (!tinterlace->cur);
00333
00334 return 0;
00335 }
00336
00337 static const AVFilterPad tinterlace_inputs[] = {
00338 {
00339 .name = "default",
00340 .type = AVMEDIA_TYPE_VIDEO,
00341 .filter_frame = filter_frame,
00342 },
00343 { NULL }
00344 };
00345
00346 static const AVFilterPad tinterlace_outputs[] = {
00347 {
00348 .name = "default",
00349 .type = AVMEDIA_TYPE_VIDEO,
00350 .config_props = config_out_props,
00351 .request_frame = request_frame,
00352 },
00353 { NULL }
00354 };
00355
00356 AVFilter avfilter_vf_tinterlace = {
00357 .name = "tinterlace",
00358 .description = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
00359 .priv_size = sizeof(TInterlaceContext),
00360 .init = init,
00361 .uninit = uninit,
00362 .query_formats = query_formats,
00363 .inputs = tinterlace_inputs,
00364 .outputs = tinterlace_outputs,
00365 .priv_class = &tinterlace_class,
00366 };