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/opt.h" 29#include "libavutil/imgutils.h" 30#include "libavutil/avassert.h" 31#include "avfilter.h" 32#include "internal.h" 33 34enum TInterlaceMode { 35 MODE_MERGE = 0, 36 MODE_DROP_EVEN, 37 MODE_DROP_ODD, 38 MODE_PAD, 39 MODE_INTERLEAVE_TOP, 40 MODE_INTERLEAVE_BOTTOM, 41 MODE_INTERLACEX2, 42 MODE_NB, 43}; 44 45typedef struct { 46 const AVClass *class; 47 enum TInterlaceMode mode; ///< interlace mode selected 48 int flags; ///< flags affecting interlacing algorithm 49 int frame; ///< number of the output frame 50 int vsub; ///< chroma vertical subsampling 51 AVFrame *cur; 52 AVFrame *next; 53 uint8_t *black_data[4]; ///< buffer used to fill padded lines 54 int black_linesize[4]; 55} TInterlaceContext; 56 57#define OFFSET(x) offsetof(TInterlaceContext, x) 58#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM 59#define TINTERLACE_FLAG_VLPF 01 60 61static const AVOption tinterlace_options[] = { 62 {"mode", "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, "mode"}, 63 {"merge", "merge fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE}, INT_MIN, INT_MAX, FLAGS, "mode"}, 64 {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN}, INT_MIN, INT_MAX, FLAGS, "mode"}, 65 {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD}, INT_MIN, INT_MAX, FLAGS, "mode"}, 66 {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD}, INT_MIN, INT_MAX, FLAGS, "mode"}, 67 {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP}, INT_MIN, INT_MAX, FLAGS, "mode"}, 68 {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "mode"}, 69 {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2}, INT_MIN, INT_MAX, FLAGS, "mode"}, 70 71 {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, 0, "flags" }, 72 {"low_pass_filter", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, 73 {"vlpf", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, 74 75 {NULL} 76}; 77 78AVFILTER_DEFINE_CLASS(tinterlace); 79 80#define FULL_SCALE_YUVJ_FORMATS \ 81 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P 82 83static enum AVPixelFormat full_scale_yuvj_pix_fmts[] = { 84 FULL_SCALE_YUVJ_FORMATS, AV_PIX_FMT_NONE 85}; 86 87static int query_formats(AVFilterContext *ctx) 88{ 89 static const enum AVPixelFormat pix_fmts[] = { 90 AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, 91 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, 92 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, 93 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, 94 AV_PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS, 95 AV_PIX_FMT_NONE 96 }; 97 98 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); 99 return 0; 100} 101 102static av_cold void uninit(AVFilterContext *ctx) 103{ 104 TInterlaceContext *tinterlace = ctx->priv; 105 106 av_frame_free(&tinterlace->cur ); 107 av_frame_free(&tinterlace->next); 108 av_freep(&tinterlace->black_data[0]); 109} 110 111static int config_out_props(AVFilterLink *outlink) 112{ 113 AVFilterContext *ctx = outlink->src; 114 AVFilterLink *inlink = outlink->src->inputs[0]; 115 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); 116 TInterlaceContext *tinterlace = ctx->priv; 117 118 tinterlace->vsub = desc->log2_chroma_h; 119 outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP; 120 outlink->w = inlink->w; 121 outlink->h = tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD ? 122 inlink->h*2 : inlink->h; 123 124 if (tinterlace->mode == MODE_PAD) { 125 uint8_t black[4] = { 16, 128, 128, 16 }; 126 int i, ret; 127 if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts)) 128 black[0] = black[3] = 0; 129 ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize, 130 outlink->w, outlink->h, outlink->format, 1); 131 if (ret < 0) 132 return ret; 133 134 /* fill black picture with black */ 135 for (i = 0; i < 4 && tinterlace->black_data[i]; i++) { 136 int h = i == 1 || i == 2 ? FF_CEIL_RSHIFT(outlink->h, desc->log2_chroma_h) : outlink->h; 137 memset(tinterlace->black_data[i], black[i], 138 tinterlace->black_linesize[i] * h); 139 } 140 } 141 if ((tinterlace->flags & TINTERLACE_FLAG_VLPF) 142 && !(tinterlace->mode == MODE_INTERLEAVE_TOP 143 || tinterlace->mode == MODE_INTERLEAVE_BOTTOM)) { 144 av_log(ctx, AV_LOG_WARNING, "low_pass_filter flag ignored with mode %d\n", 145 tinterlace->mode); 146 tinterlace->flags &= ~TINTERLACE_FLAG_VLPF; 147 } 148 if (tinterlace->mode == MODE_INTERLACEX2) { 149 outlink->time_base.num = inlink->time_base.num; 150 outlink->time_base.den = inlink->time_base.den * 2; 151 outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){2,1}); 152 } 153 154 av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", 155 tinterlace->mode, (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "on" : "off", 156 inlink->h, outlink->h); 157 158 return 0; 159} 160 161#define FIELD_UPPER 0 162#define FIELD_LOWER 1 163#define FIELD_UPPER_AND_LOWER 2 164 165/** 166 * Copy picture field from src to dst. 167 * 168 * @param src_field copy from upper, lower field or both 169 * @param interleave leave a padding line between each copied line 170 * @param dst_field copy to upper or lower field, 171 * only meaningful when interleave is selected 172 * @param flags context flags 173 */ 174static inline 175void copy_picture_field(uint8_t *dst[4], int dst_linesize[4], 176 const uint8_t *src[4], int src_linesize[4], 177 enum AVPixelFormat format, int w, int src_h, 178 int src_field, int interleave, int dst_field, 179 int flags) 180{ 181 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format); 182 int plane, vsub = desc->log2_chroma_h; 183 int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2; 184 int h, i; 185 186 for (plane = 0; plane < desc->nb_components; plane++) { 187 int lines = plane == 1 || plane == 2 ? FF_CEIL_RSHIFT(src_h, vsub) : src_h; 188 int linesize = av_image_get_linesize(format, w, plane); 189 uint8_t *dstp = dst[plane]; 190 const uint8_t *srcp = src[plane]; 191 192 if (linesize < 0) 193 return; 194 195 lines = (lines + (src_field == FIELD_UPPER)) / k; 196 if (src_field == FIELD_LOWER) 197 srcp += src_linesize[plane]; 198 if (interleave && dst_field == FIELD_LOWER) 199 dstp += dst_linesize[plane]; 200 if (flags & TINTERLACE_FLAG_VLPF) { 201 // Low-pass filtering is required when creating an interlaced destination from 202 // a progressive source which contains high-frequency vertical detail. 203 // Filtering will reduce interlace 'twitter' and Moire patterning. 204 int srcp_linesize = src_linesize[plane] * k; 205 int dstp_linesize = dst_linesize[plane] * (interleave ? 2 : 1); 206 for (h = lines; h > 0; h--) { 207 const uint8_t *srcp_above = srcp - src_linesize[plane]; 208 const uint8_t *srcp_below = srcp + src_linesize[plane]; 209 if (h == lines) srcp_above = srcp; // there is no line above 210 if (h == 1) srcp_below = srcp; // there is no line below 211 for (i = 0; i < linesize; i++) { 212 // this calculation is an integer representation of 213 // '0.5 * current + 0.25 * above + 0.25 * below' 214 // '1 +' is for rounding. */ 215 dstp[i] = (1 + srcp[i] + srcp[i] + srcp_above[i] + srcp_below[i]) >> 2; 216 } 217 dstp += dstp_linesize; 218 srcp += srcp_linesize; 219 } 220 } else { 221 av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1), 222 srcp, src_linesize[plane]*k, linesize, lines); 223 } 224 } 225} 226 227static int filter_frame(AVFilterLink *inlink, AVFrame *picref) 228{ 229 AVFilterContext *ctx = inlink->dst; 230 AVFilterLink *outlink = ctx->outputs[0]; 231 TInterlaceContext *tinterlace = ctx->priv; 232 AVFrame *cur, *next, *out; 233 int field, tff, ret; 234 235 av_frame_free(&tinterlace->cur); 236 tinterlace->cur = tinterlace->next; 237 tinterlace->next = picref; 238 239 cur = tinterlace->cur; 240 next = tinterlace->next; 241 /* we need at least two frames */ 242 if (!tinterlace->cur) 243 return 0; 244 245 switch (tinterlace->mode) { 246 case MODE_MERGE: /* move the odd frame into the upper field of the new image, even into 247 * the lower field, generating a double-height video at half framerate */ 248 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 249 if (!out) 250 return AVERROR(ENOMEM); 251 av_frame_copy_props(out, cur); 252 out->height = outlink->h; 253 out->interlaced_frame = 1; 254 out->top_field_first = 1; 255 256 /* write odd frame lines into the upper field of the new frame */ 257 copy_picture_field(out->data, out->linesize, 258 (const uint8_t **)cur->data, cur->linesize, 259 inlink->format, inlink->w, inlink->h, 260 FIELD_UPPER_AND_LOWER, 1, FIELD_UPPER, tinterlace->flags); 261 /* write even frame lines into the lower field of the new frame */ 262 copy_picture_field(out->data, out->linesize, 263 (const uint8_t **)next->data, next->linesize, 264 inlink->format, inlink->w, inlink->h, 265 FIELD_UPPER_AND_LOWER, 1, FIELD_LOWER, tinterlace->flags); 266 av_frame_free(&tinterlace->next); 267 break; 268 269 case MODE_DROP_ODD: /* only output even frames, odd frames are dropped; height unchanged, half framerate */ 270 case MODE_DROP_EVEN: /* only output odd frames, even frames are dropped; height unchanged, half framerate */ 271 out = av_frame_clone(tinterlace->mode == MODE_DROP_EVEN ? cur : next); 272 if (!out) 273 return AVERROR(ENOMEM); 274 av_frame_free(&tinterlace->next); 275 break; 276 277 case MODE_PAD: /* expand each frame to double height, but pad alternate 278 * lines with black; framerate unchanged */ 279 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 280 if (!out) 281 return AVERROR(ENOMEM); 282 av_frame_copy_props(out, cur); 283 out->height = outlink->h; 284 285 field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER; 286 /* copy upper and lower fields */ 287 copy_picture_field(out->data, out->linesize, 288 (const uint8_t **)cur->data, cur->linesize, 289 inlink->format, inlink->w, inlink->h, 290 FIELD_UPPER_AND_LOWER, 1, field, tinterlace->flags); 291 /* pad with black the other field */ 292 copy_picture_field(out->data, out->linesize, 293 (const uint8_t **)tinterlace->black_data, tinterlace->black_linesize, 294 inlink->format, inlink->w, inlink->h, 295 FIELD_UPPER_AND_LOWER, 1, !field, tinterlace->flags); 296 break; 297 298 /* interleave upper/lower lines from odd frames with lower/upper lines from even frames, 299 * halving the frame rate and preserving image height */ 300 case MODE_INTERLEAVE_TOP: /* top field first */ 301 case MODE_INTERLEAVE_BOTTOM: /* bottom field first */ 302 tff = tinterlace->mode == MODE_INTERLEAVE_TOP; 303 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 304 if (!out) 305 return AVERROR(ENOMEM); 306 av_frame_copy_props(out, cur); 307 out->interlaced_frame = 1; 308 out->top_field_first = tff; 309 310 /* copy upper/lower field from cur */ 311 copy_picture_field(out->data, out->linesize, 312 (const uint8_t **)cur->data, cur->linesize, 313 inlink->format, inlink->w, inlink->h, 314 tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, 315 tinterlace->flags); 316 /* copy lower/upper field from next */ 317 copy_picture_field(out->data, out->linesize, 318 (const uint8_t **)next->data, next->linesize, 319 inlink->format, inlink->w, inlink->h, 320 tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, 321 tinterlace->flags); 322 av_frame_free(&tinterlace->next); 323 break; 324 case MODE_INTERLACEX2: /* re-interlace preserving image height, double frame rate */ 325 /* output current frame first */ 326 out = av_frame_clone(cur); 327 if (!out) 328 return AVERROR(ENOMEM); 329 out->interlaced_frame = 1; 330 if (cur->pts != AV_NOPTS_VALUE) 331 out->pts = cur->pts*2; 332 333 if ((ret = ff_filter_frame(outlink, out)) < 0) 334 return ret; 335 336 /* output mix of current and next frame */ 337 tff = next->top_field_first; 338 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 339 if (!out) 340 return AVERROR(ENOMEM); 341 av_frame_copy_props(out, next); 342 out->interlaced_frame = 1; 343 344 if (next->pts != AV_NOPTS_VALUE && cur->pts != AV_NOPTS_VALUE) 345 out->pts = cur->pts + next->pts; 346 else 347 out->pts = AV_NOPTS_VALUE; 348 /* write current frame second field lines into the second field of the new frame */ 349 copy_picture_field(out->data, out->linesize, 350 (const uint8_t **)cur->data, cur->linesize, 351 inlink->format, inlink->w, inlink->h, 352 tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, 353 tinterlace->flags); 354 /* write next frame first field lines into the first field of the new frame */ 355 copy_picture_field(out->data, out->linesize, 356 (const uint8_t **)next->data, next->linesize, 357 inlink->format, inlink->w, inlink->h, 358 tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, 359 tinterlace->flags); 360 break; 361 default: 362 av_assert0(0); 363 } 364 365 ret = ff_filter_frame(outlink, out); 366 tinterlace->frame++; 367 368 return ret; 369} 370 371static const AVFilterPad tinterlace_inputs[] = { 372 { 373 .name = "default", 374 .type = AVMEDIA_TYPE_VIDEO, 375 .filter_frame = filter_frame, 376 }, 377 { NULL } 378}; 379 380static const AVFilterPad tinterlace_outputs[] = { 381 { 382 .name = "default", 383 .type = AVMEDIA_TYPE_VIDEO, 384 .config_props = config_out_props, 385 }, 386 { NULL } 387}; 388 389AVFilter ff_vf_tinterlace = { 390 .name = "tinterlace", 391 .description = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."), 392 .priv_size = sizeof(TInterlaceContext), 393 .uninit = uninit, 394 .query_formats = query_formats, 395 .inputs = tinterlace_inputs, 396 .outputs = tinterlace_outputs, 397 .priv_class = &tinterlace_class, 398}; 399