diff options
author | Nicholas Robbins <nickrobbins@yahoo.com> | 2014-02-14 16:15:44 -0500 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2014-02-16 17:43:58 +0100 |
commit | b4d308c04f7ad0ae76e32f4521d16e39dee3f521 (patch) | |
tree | 0368ba39755684eebc593e8663559fccff3e5579 | |
parent | 18f94df8af04f2c02a25a7dec512289feff6517f (diff) | |
download | ffmpeg-b4d308c04f7ad0ae76e32f4521d16e39dee3f521.tar.gz |
lavfi: adding dejudder filter to remove judder produced by partially telecined material.
Signed-off-by: Nicholas Robbins <nickrobbins@yahoo.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r-- | Changelog | 1 | ||||
-rw-r--r-- | doc/filters.texi | 33 | ||||
-rw-r--r-- | libavfilter/Makefile | 1 | ||||
-rw-r--r-- | libavfilter/allfilters.c | 1 | ||||
-rw-r--r-- | libavfilter/vf_dejudder.c | 187 |
5 files changed, 223 insertions, 0 deletions
@@ -27,6 +27,7 @@ version <next> - Mirillis FIC video decoder - Support DNx444 - libx265 encoder +- dejudder filter version 2.1: diff --git a/doc/filters.texi b/doc/filters.texi index 1d3c30b0d1..352d49eb19 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -3215,6 +3215,38 @@ Set whether or not chroma is considered in the metric calculations. Default is @code{1}. @end table +@section dejudder + +Remove judder produced by partially interlaced telecined content. + +Judder can be introduced, for instance, by @ref{pullup} filter. If the original +source was partially telecined content then the output of @code{pullup,dejudder} +will have a variable frame rate. May change the recorded frame rate of the +container. Aside from that change, this filter will not affect constant frame +rate video. + +The option available in this filter is: +@table @option + +@item cycle +Specify the length of the window over which the judder repeats. + +Accepts any interger greater than 1. Useful values are: +@table @samp + +@item 4 +If the original was telecined from 24 to 30 fps (Film to NTSC). + +@item 5 +If the original was telecined from 25 to 30 fps (PAL to NTSC). + +@item 20 +If a mixture of the two. +@end table + +The default is @samp{4}. +@end table + @section delogo Suppress a TV station logo by a simple interpolation of the surrounding @@ -6633,6 +6665,7 @@ On this example the input file being processed is compared with the reference file @file{ref_movie.mpg}. The PSNR of each individual frame is stored in @file{stats.log}. +@anchor{pullup} @section pullup Pulldown reversal (inverse telecine) filter, capable of handling mixed diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 05ec5f2cb9..c36b67ec5a 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -127,6 +127,7 @@ OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o OBJS-$(CONFIG_CURVES_FILTER) += vf_curves.o OBJS-$(CONFIG_DCTDNOIZ_FILTER) += vf_dctdnoiz.o OBJS-$(CONFIG_DECIMATE_FILTER) += vf_decimate.o +OBJS-$(CONFIG_DEJUDDER_FILTER) += vf_dejudder.o OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o OBJS-$(CONFIG_DESHAKE_FILTER) += vf_deshake.o OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 8e8df7bbc5..d042b64ce3 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -122,6 +122,7 @@ void avfilter_register_all(void) REGISTER_FILTER(CURVES, curves, vf); REGISTER_FILTER(DCTDNOIZ, dctdnoiz, vf); REGISTER_FILTER(DECIMATE, decimate, vf); + REGISTER_FILTER(DEJUDDER, dejudder, vf); REGISTER_FILTER(DELOGO, delogo, vf); REGISTER_FILTER(DESHAKE, deshake, vf); REGISTER_FILTER(DRAWBOX, drawbox, vf); diff --git a/libavfilter/vf_dejudder.c b/libavfilter/vf_dejudder.c new file mode 100644 index 0000000000..1d96c394e5 --- /dev/null +++ b/libavfilter/vf_dejudder.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2014 Nicholas Robbins + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * remove judder in video stream + * + * Algorithm: + * - If the old packets had PTS of old_pts[i]. Replace these with new + * value based on the running average of the last n=cycle frames. So + * + * new_pts[i] = Sum(k=i-n+1, i, old_pts[k])/n + * + (old_pts[i]-old_pts[i-n])*(n-1)/2n + * + * For any repeating pattern of length n of judder this will produce + * an even progression of PTS's. + * + * - In order to avoid calculating this sum ever frame, a running tally + * is maintained in ctx->new_pts. Each frame the new term at the start + * of the sum is added, the one and the end is removed, and the offset + * terms (second line in formula above) are recalculated. + * + * - To aid in this a ringbuffer of the last n-2 PTS's is maintained in + * ctx->ringbuff. With the indices of the first two and last two entries + * stored in i1, i2, i3, & i4. + * + * - To ensure that the new PTS's are integers, time_base is divided + * by 2n. This removes the division in the new_pts calculation. + * + * - frame_rate is also multiplied by 2n to allow the frames to fall + * where they may in what may now be a VFR output. This produces more + * even output then setting frame_rate=1/0 in practice. + */ + +#include "libavutil/opt.h" +#include "libavutil/mathematics.h" +#include "avfilter.h" +#include "internal.h" +#include "video.h" + +typedef struct { + const AVClass *class; + int64_t *ringbuff; + int i1, i2, i3, i4; + int64_t new_pts; + int start_count; + + /* options */ + int cycle; +} DejudderContext; + +#define OFFSET(x) offsetof(DejudderContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption dejudder_options[] = { + {"cycle", "set the length of the cycle to use for dejuddering", + OFFSET(cycle), AV_OPT_TYPE_INT, {.i64 = 4}, 2, 240, .flags = FLAGS}, + {NULL} +}; + +AVFILTER_DEFINE_CLASS(dejudder); + +static int config_out_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + DejudderContext *dj = ctx->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->time_base = av_mul_q(inlink->time_base, av_make_q(1, 2 * dj->cycle)); + outlink->frame_rate = av_mul_q(inlink->frame_rate, av_make_q(2 * dj->cycle, 1)); + + av_log(ctx, AV_LOG_VERBOSE, "cycle:%d\n", dj->cycle); + + return 0; +} + +static av_cold int dejudder_init(AVFilterContext *ctx) +{ + DejudderContext *dj = ctx->priv; + + dj->ringbuff = av_mallocz(sizeof(*dj->ringbuff) * (dj->cycle+2)); + if (!dj->ringbuff) + return AVERROR(ENOMEM); + + dj->new_pts = 0; + dj->i1 = 0; + dj->i2 = 1; + dj->i3 = 2; + dj->i4 = 3; + dj->start_count = dj->cycle + 2; + + return 0; +} + +static av_cold void dejudder_uninit(AVFilterContext *ctx) +{ + DejudderContext *dj = ctx->priv; + + av_freep(&(dj->ringbuff)); +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + int k; + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + DejudderContext *dj = ctx->priv; + int64_t *judbuff = dj->ringbuff; + int64_t next_pts = frame->pts; + int64_t offset; + + if (next_pts == AV_NOPTS_VALUE) + return ff_filter_frame(outlink, frame); + + if (dj->start_count) { + dj->start_count--; + dj->new_pts = next_pts * 2 * dj->cycle; + } else { + if (next_pts < judbuff[dj->i2]) { + offset = next_pts + judbuff[dj->i3] - judbuff[dj->i4] - judbuff[dj->i1]; + for (k = 0; k < dj->cycle + 2; k++) + judbuff[k] += offset; + } + dj->new_pts += (dj->cycle - 1) * (judbuff[dj->i3] - judbuff[dj->i1]) + + (dj->cycle + 1) * (next_pts - judbuff[dj->i4]); + } + + judbuff[dj->i2] = next_pts; + dj->i1 = dj->i2; + dj->i2 = dj->i3; + dj->i3 = dj->i4; + dj->i4 = (dj->i4 + 1) % (dj->cycle + 2); + + frame->pts = dj->new_pts; + + for (k = 0; k < dj->cycle + 2; k++) + av_log(ctx, AV_LOG_DEBUG, "%"PRId64"\t", judbuff[k]); + av_log(ctx, AV_LOG_DEBUG, "next=%"PRId64", new=%"PRId64"\n", next_pts, frame->pts); + + return ff_filter_frame(outlink, frame); +} + +static const AVFilterPad dejudder_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad dejudder_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_out_props, + }, + { NULL } +}; + +AVFilter ff_vf_dejudder = { + .name = "dejudder", + .description = NULL_IF_CONFIG_SMALL("Remove judder produced by pullup."), + .priv_size = sizeof(DejudderContext), + .priv_class = &dejudder_class, + .inputs = dejudder_inputs, + .outputs = dejudder_outputs, + .init = dejudder_init, + .uninit = dejudder_uninit, +}; |