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 /libavfilter/vf_dejudder.c | |
parent | 18f94df8af04f2c02a25a7dec512289feff6517f (diff) |
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>
Diffstat (limited to 'libavfilter/vf_dejudder.c')
-rw-r--r-- | libavfilter/vf_dejudder.c | 187 |
1 files changed, 187 insertions, 0 deletions
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, +}; |