diff mbox

[FFmpeg-devel,3/5] Renamed reinterlace to tinterlace

Message ID CALGJd-+Du_EtAEiPU_PiuPuqrFomNzk7wC5vOscVyZ39yPps8w@mail.gmail.com
State Superseded
Headers show

Commit Message

Vasile Toncu July 24, 2018, 10:17 a.m. UTC
Fixed tabs.
Thank you for the feedback.

On Fri, Jul 20, 2018 at 7:27 PM, Paul B Mahol <onemda@gmail.com> wrote:

> On 7/20/18, Vasile Toncu <vasile.toncu@tremend.com> wrote:
> > Hi,
> >
> > Did you have the time to take a look at patches 2 and 3? It's everything
> > fine?
>
> Yes and no, patches have tabs. Please fix that.
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Comments

Vasile Toncu July 25, 2018, 3:11 p.m. UTC | #1
What would be the next steps?

On Tue, Jul 24, 2018 at 12:17 PM, Vasile Toncu <vasile.toncu@tremend.com>
wrote:

> Fixed tabs.
> Thank you for the feedback.
>
> On Fri, Jul 20, 2018 at 7:27 PM, Paul B Mahol <onemda@gmail.com> wrote:
>
>> On 7/20/18, Vasile Toncu <vasile.toncu@tremend.com> wrote:
>> > Hi,
>> >
>> > Did you have the time to take a look at patches 2 and 3? It's everything
>> > fine?
>>
>> Yes and no, patches have tabs. Please fix that.
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>
>
Paul B Mahol July 25, 2018, 3:16 p.m. UTC | #2
On 7/25/18, Vasile Toncu <vasile.toncu@tremend.com> wrote:
> What would be the next steps?
>

Ping patch every second.
Thomas Mundt July 25, 2018, 3:36 p.m. UTC | #3
2018-07-25 17:11 GMT+02:00 Vasile Toncu <vasile.toncu@tremend.com>:

> What would be the next steps?
>
> On Tue, Jul 24, 2018 at 12:17 PM, Vasile Toncu <vasile.toncu@tremend.com>
> wrote:
>
> > Fixed tabs.
> > Thank you for the feedback.
> >
> > On Fri, Jul 20, 2018 at 7:27 PM, Paul B Mahol <onemda@gmail.com> wrote:
> >
> >> On 7/20/18, Vasile Toncu <vasile.toncu@tremend.com> wrote:
> >> > Hi,
> >> >
> >> > Did you have the time to take a look at patches 2 and 3? It's
> everything
> >> > fine?
> >>
> >> Yes and no, patches have tabs. Please fix that.
>

Please be patient. I´m just back from vacation and full of tasks at work.
You took months for the revised patches. Now I need to work into it first.
Thomas Mundt July 28, 2018, 3:04 p.m. UTC | #4
Hi,

2018-07-24 12:17 GMT+02:00 Vasile Toncu <vasile.toncu@tremend.com>:

> Fixed tabs.
> Thank you for the feedback.
>

> {....}
> +    case MODE_INTERLEAVE_BOTTOM:
> +    case MODE_INTERLEAVE_TOP:
> +        y = y * 2;
> +
> +        if (tinterlace->flags & FLAG_VLPF || tinterlace->flags &
> FLAG_CVLPF) {
> +
> +            int lines, cols, cvlfp;
> +            AVFrame *from_frame;
> +            uint8_t *from, *to;
> +            int from_step, to_step;
> +
> +            lines = (tinterlace_mode == MODE_INTERLEAVE_TOP) ? (2 *
> out->height / y + 1) / 2 : (2 * out->height / y + 0) / 2;
> +            cols = out->width / x;
> +            from_frame = first;
> +            from = from_frame->data[plane];
> +            to = out->data[plane];
> +
> +            if (tinterlace_mode == MODE_INTERLEAVE_BOTTOM) {
> +                from = from + from_frame->linesize[plane];
> +                to = to + out->linesize[plane];
> +            }
> +
> +            from_step = 2 * from_frame->linesize[plane];
> +            to_step = 2 * out->linesize[plane];
> +
> +            // when i = lines - aka first line
> +            tinterlace->lowpass_line(to, cols, from,
> from_frame->linesize[plane], 0, clip_max);
> +            to += to_step;
> +            from += from_step;
> +
> +            cvlfp = !!(tinterlace->flags & FLAG_CVLPF);
> +            if (cvlfp) {
> +                tinterlace->lowpass_line(to, cols, from,
> from_frame->linesize[plane], 0, clip_max);
> +                to += to_step;
> +                from += from_step;
> +            }
> +
> +            for (i = lines - 2 - 2 * cvlfp; i; i--) {
> +                tinterlace->lowpass_line(to, cols, from,
> from_frame->linesize[plane], -from_frame->linesize[plane], clip_max);
> +                to += to_step;
> +                from += from_step;
> +            }
> +
> +            // when i == 1 - aka last line
> +            tinterlace->lowpass_line(to, cols, from, 0,
> -from_frame->linesize[plane], clip_max);
> +            to += to_step;
> +            from += from_step;
> +
> +            if (cvlfp) {
> +                tinterlace->lowpass_line(to, cols, from, 0,
> -from_frame->linesize[plane], clip_max);
> +                to += to_step;
> +                from += from_step;
> +            }
> +
> +
> +            lines = (tinterlace_mode == MODE_INTERLEAVE_BOTTOM) ? ((2 *
> out->height / y) + 1) / 2 : (2 * out->height / y + 0) / 2;
> +            cols = out->width / x;
> +            from_frame = second;
> +            from = from_frame->data[plane];
> +            to = out->data[plane];
> +
> +            if (tinterlace_mode == MODE_INTERLEAVE_TOP) {
> +                from = from + from_frame->linesize[plane];
> +                to = to + out->linesize[plane];
>              }
> +
> +            from_step = 2 * from_frame->linesize[plane];
> +            to_step = 2 * out->linesize[plane];
> +
> +            // when i = lines
> +            tinterlace->lowpass_line(to, cols, from,
> from_frame->linesize[plane], 0, clip_max);
> +            to += to_step;
> +            from += from_step;
> +
> +            if (cvlfp) {
> +                tinterlace->lowpass_line(to, cols, from,
> from_frame->linesize[plane], 0, clip_max);
> +                to += to_step;
> +                from += from_step;
> +            }
> +
> +
> +            for (i = lines - 2 - 2 * cvlfp; i; i--) {
> +                tinterlace->lowpass_line(to, cols, from,
> from_frame->linesize[plane], -from_frame->linesize[plane], clip_max);
> +                to += to_step;
> +                from += from_step;
> +            }
> +
> +            // when i == 1
> +            tinterlace->lowpass_line(to, cols, from, 0,
> -from_frame->linesize[plane], clip_max);
> +            to += to_step;
> +            from += from_step;
> +
> +            if (cvlfp) {
> +                tinterlace->lowpass_line(to, cols, from, 0,
> -from_frame->linesize[plane], clip_max);
> +                to += to_step;
> +                from += from_step;
> +            }
>

Compared to the one simple "for"-loop in the GPL version, this looks very
complicated. Maybe it´s ok here to just keep the original code since it has
been modified in the past anyway.
But at least you need to factor it out for not having the same code two
times in a row.

{....}
> +    case MODE_MERGE_TFF:
> +    case MODE_MERGE_BFF:
> +        offset1 = (tinterlace_mode == MODE_MERGE_TFF) ? 0 :
> out->linesize[plane];
> +        offset2 = (tinterlace_mode == MODE_MERGE_TFF) ?
> out->linesize[plane] : 0;
> +
> +        av_image_copy_plane(out->data[plane] + offset1, 2 *
> out->linesize[plane],
> +            first->data[plane], first->linesize[plane], first->width / x,
> first->height / y);
> +        av_image_copy_plane(out->data[plane] + offset2, 2 *
> out->linesize[plane],
> +            second->data[plane], second->linesize[plane], second->width /
> x, second->height / y);
> +        break;
>

These are new functions, independent from the license change. Please put
them in a separate patch together with documentation and fate tests.


> {....}
> +static TInterlaceThreadData *get_TInterlaceThreadData(AVFrame *out,
> AVFrame *first, AVFrame *second,
> +                int plane, TInterlaceContext *tinterlace,
> +                int scale_w_plane12_factor,
> +                int scale_h_plane12_factor)
>  {
> -    AVFilterContext *ctx = inlink->dst;
> -    AVFilterLink *outlink = ctx->outputs[0];
> -    TInterlaceContext *tinterlace = ctx->priv;
> -    AVFrame *cur, *next, *out;
> -    int field, tff, ret;
> -
> -    av_frame_free(&tinterlace->cur);
> -    tinterlace->cur  = tinterlace->next;
> -    tinterlace->next = picref;
> -
> -    cur = tinterlace->cur;
> -    next = tinterlace->next;
> -    /* we need at least two frames */
> -    if (!tinterlace->cur)
> -        return 0;
> -
> -    switch (tinterlace->mode) {
> -    case MODE_MERGEX2: /* move the odd frame into the upper field of the
> new image, even into
> -                        * the lower field, generating a double-height
> video at same framerate */
> -    case MODE_MERGE: /* move the odd frame into the upper field of the
> new image, even into
> -             * the lower field, generating a double-height video at half
> framerate */
> -        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> +    TInterlaceThreadData *rtd = &((TInterlaceThreadData
> *)tinterlace->thread_data)[plane];
> +
> +    if (!rtd)
> +        return rtd;
> +
> +    rtd->out = out;
> +    rtd->first = first;
> +    rtd->second = second;
> +    rtd->plane = plane;
> +    rtd->tinterlace = tinterlace;
> +    rtd->scale_h_plane12_factor = scale_h_plane12_factor;
> +    rtd->scale_w_plane12_factor = scale_w_plane12_factor;
> +
> +    return rtd;
> +}
>

Threading support is also independent from the lincense change and should
be added by a separate patch.

  {....}
>  AVFilter ff_vf_tinterlace = {
>      .name          = "tinterlace",
> -    .description   = NULL_IF_CONFIG_SMALL("Perform temporal field
> interlacing."),
> +    .description   = NULL_IF_CONFIG_SMALL("Various interlace frame
> manipulations"),
>      .priv_size     = sizeof(TInterlaceContext),
> +    .init          = init,
>      .uninit        = uninit,
>      .query_formats = query_formats,
>      .inputs        = tinterlace_inputs,
>      .outputs       = tinterlace_outputs,
>      .priv_class    = &tinterlace_class,
> +    .flags         = AVFILTER_FLAG_SLICE_THREADS |
> AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
>  };
>
> -
>  AVFilter ff_vf_interlace = {
>      .name          = "interlace",
>      .description   = NULL_IF_CONFIG_SMALL("Convert progressive video into
> interlaced."),
>      .priv_size     = sizeof(TInterlaceContext),
> +    .init          = init,
>      .uninit        = uninit,
>      .query_formats = query_formats,
>      .inputs        = tinterlace_inputs,
>

When you add new flags to vf_tinterlace, please also add them to
vf_interlace.

  {....}
> +#elif /* CONFIG_GPL */
> +
> +av_cold void ff_tinterlace_init_x86(ReInterlaceContext *s) {}
>

ReInterlace?

+
> +#endif /* CONFIG_GPL */
> --
> 2.17.1
>

The rest looks ok to me. Compiler warnings are gone and fate tests passes.
However, as stated before, I´m not able to judge the requirements for the
license change.

Regards,
Thomas
diff mbox

Patch

From 965cb710db38ccd57213648c01ba351dc4076293 Mon Sep 17 00:00:00 2001
From: Vasile Toncu <vasile.toncu@tremend.com>
Date: Tue, 24 Jul 2018 12:02:16 +0200
Subject: [PATCH] Patch 3 - Move reinterlace to tinterlace

---
 configure                             |   1 -
 libavfilter/Makefile                  |   1 -
 libavfilter/allfilters.c              |   1 -
 libavfilter/reinterlace.h             | 105 ----
 libavfilter/tinterlace.h              | 111 ++--
 libavfilter/vf_reinterlace.c          | 775 ------------------------
 libavfilter/vf_tinterlace.c           | 824 +++++++++++++++++---------
 libavfilter/x86/Makefile              |   1 -
 libavfilter/x86/vf_reinterlace_init.c | 102 ----
 libavfilter/x86/vf_tinterlace_init.c  |  41 +-
 10 files changed, 633 insertions(+), 1329 deletions(-)
 delete mode 100644 libavfilter/reinterlace.h
 delete mode 100644 libavfilter/vf_reinterlace.c
 delete mode 100644 libavfilter/x86/vf_reinterlace_init.c

diff --git a/configure b/configure
index 5783407bdc..0a33a8dcb3 100755
--- a/configure
+++ b/configure
@@ -3415,7 +3415,6 @@  stereo3d_filter_deps="gpl"
 subtitles_filter_deps="avformat avcodec libass"
 super2xsai_filter_deps="gpl"
 pixfmts_super2xsai_test_deps="super2xsai_filter"
-tinterlace_filter_deps="gpl"
 tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
 tonemap_filter_deps="const_nan"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 49fad08290..5d4549e24c 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -245,7 +245,6 @@  OBJS-$(CONFIG_IDET_FILTER)                   += vf_idet.o
 OBJS-$(CONFIG_IL_FILTER)                     += vf_il.o
 OBJS-$(CONFIG_INFLATE_FILTER)                += vf_neighbor.o
 OBJS-$(CONFIG_INTERLACE_FILTER)              += vf_tinterlace.o
-OBJS-$(CONFIG_REINTERLACE_FILTER)            += vf_reinterlace.o
 OBJS-$(CONFIG_INTERLEAVE_FILTER)             += f_interleave.o
 OBJS-$(CONFIG_KERNDEINT_FILTER)              += vf_kerndeint.o
 OBJS-$(CONFIG_LENSCORRECTION_FILTER)         += vf_lenscorrection.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 874d25e72f..521bc53164 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -347,7 +347,6 @@  extern AVFilter ff_vf_thumbnail;
 extern AVFilter ff_vf_thumbnail_cuda;
 extern AVFilter ff_vf_tile;
 extern AVFilter ff_vf_tinterlace;
-extern AVFilter ff_vf_reinterlace;
 extern AVFilter ff_vf_tlut2;
 extern AVFilter ff_vf_tmix;
 extern AVFilter ff_vf_tonemap;
diff --git a/libavfilter/reinterlace.h b/libavfilter/reinterlace.h
deleted file mode 100644
index cb512c7639..0000000000
--- a/libavfilter/reinterlace.h
+++ /dev/null
@@ -1,105 +0,0 @@ 
-/*
- * Copyright (c) 2017 Vasile Toncu <vasile.toncu@tremend.com>
- * Copyright (c) 2017 Thomas Mundt <tmundt75@gmail.com>
- *
- * 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
- * Reinterlace filter
- */
-
-#ifndef AVFILTER_REINTERLACE_H
-#define AVFILTER_REINTERLACE_H
-
-#include "avfilter.h"
-#include "formats.h"
-#include "internal.h"
-#include "video.h"
-#include "libavutil/avassert.h"
-#include "libavutil/imgutils.h"
-#include "libavutil/opt.h"
-#include "libavutil/pixdesc.h"
-#include "libavutil/bswap.h"
-
-enum FilterMode {
-    MODE_MERGE,
-    MODE_DROP_EVEN,
-    MODE_DROP_ODD,
-    MODE_PAD,
-    MODE_INTERLEAVE_TOP,
-    MODE_INTERLEAVE_BOTTOM,
-    MODE_INTERLACE_X2,
-    MODE_MERGE_X2,
-    MODE_MERGE_TFF,
-    MODE_MERGE_BFF,
-    MODE_NB,
- };
-
-enum FilterFlags {
-    FLAG_NOTHING    = 0x00,
-    FLAG_VLPF       = 0x01,
-    FLAG_EXACT_TB   = 0x02,
-    FLAG_CVLPF      = 0x04,
-    FLAG_NB
-};
-
-typedef struct {
-    const AVClass *class;
-    int mode;
-    int flags;
-
-    AVFrame *prev_frame, *current_frame;
-    int64_t current_frame_index;
-
-    uint8_t *black_vec[4];
-    int black_linesize[4];
-
-    int skip_next_frame;
-
-    void *thread_data;
-
-    uint8_t bit_depth;
-
-    void (*lowpass_line)(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp,
-                          ptrdiff_t mref, ptrdiff_t pref, int clip_max);
-
-    AVRational preout_time_base;
-
- } ReInterlaceContext;
-
-#if CONFIG_GPL
-void ff_reinterlace_init_x86(ReInterlaceContext *reinterlace);
-#endif
-
-#define OFFSET(x) offsetof(ReInterlaceContext, x)
-#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
-
-#define IS_ODD(value) (value & 1)
-
-typedef struct ReInterlaceThreadData {
-    AVFrame *out, *first, *second;
-    int plane;
-    ReInterlaceContext *reinterlace;
-
-    int scale_w_plane12_factor;
-    int scale_h_plane12_factor;
-
-} ReInterlaceThreadData;
-
-#endif /* AVFILTER_REINTERLACE_H */
diff --git a/libavfilter/tinterlace.h b/libavfilter/tinterlace.h
index b5c39aac52..eff9973dc9 100644
--- a/libavfilter/tinterlace.h
+++ b/libavfilter/tinterlace.h
@@ -1,72 +1,105 @@ 
 /*
- * Copyright (c) 2011 Stefano Sabatini
- * Copyright (c) 2010 Baptiste Coudurier
- * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com>
+ * Copyright (c) 2017 Vasile Toncu <vasile.toncu@tremend.com>
+ * Copyright (c) 2017 Thomas Mundt <tmundt75@gmail.com>
  *
  * This file is part of FFmpeg.
  *
- * FFmpeg is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * 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 General Public License for more details.
+ * 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 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.
+ * 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
- * temporal field interlace filter, ported from MPlayer/libmpcodecs
+ * TInterlace filter
  */
+
 #ifndef AVFILTER_TINTERLACE_H
 #define AVFILTER_TINTERLACE_H
 
-#include "libavutil/bswap.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+#include "libavutil/avassert.h"
+#include "libavutil/imgutils.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
-#include "drawutils.h"
-#include "avfilter.h"
-
-#define TINTERLACE_FLAG_VLPF 01
-#define TINTERLACE_FLAG_EXACT_TB 2
-#define TINTERLACE_FLAG_CVLPF 4
+#include "libavutil/bswap.h"
 
-enum TInterlaceMode {
-    MODE_MERGE = 0,
+enum FilterMode {
+    MODE_MERGE,
     MODE_DROP_EVEN,
     MODE_DROP_ODD,
     MODE_PAD,
     MODE_INTERLEAVE_TOP,
     MODE_INTERLEAVE_BOTTOM,
-    MODE_INTERLACEX2,
-    MODE_MERGEX2,
+    MODE_INTERLACE_X2,
+    MODE_MERGE_X2,
+    MODE_MERGE_TFF,
+    MODE_MERGE_BFF,
     MODE_NB,
+ };
+
+enum FilterFlags {
+    FLAG_NOTHING    = 0x00,
+    FLAG_VLPF       = 0x01,
+    FLAG_EXACT_TB   = 0x02,
+    FLAG_CVLPF      = 0x04,
+    FLAG_NB
 };
 
-typedef struct TInterlaceContext {
+typedef struct {
     const AVClass *class;
-    int mode;                   ///< TInterlaceMode, interlace mode selected
-    AVRational preout_time_base;
-    int flags;                  ///< flags affecting interlacing algorithm
-    int frame;                  ///< number of the output frame
-    int vsub;                   ///< chroma vertical subsampling
-    AVFrame *cur;
-    AVFrame *next;
-    uint8_t *black_data[4];     ///< buffer used to fill padded lines
+    int mode;
+    int flags;
+
+    AVFrame *prev_frame, *current_frame;
+    int64_t current_frame_index;
+
+    uint8_t *black_vec[4];
     int black_linesize[4];
-    FFDrawContext draw;
-    FFDrawColor color;
-    const AVPixFmtDescriptor *csp;
+
+    int skip_next_frame;
+
+    void *thread_data;
+
+    uint8_t bit_depth;
+
     void (*lowpass_line)(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp,
-                         ptrdiff_t mref, ptrdiff_t pref, int clip_max);
-} TInterlaceContext;
+                          ptrdiff_t mref, ptrdiff_t pref, int clip_max);
+
+    AVRational preout_time_base;
+
+ } TInterlaceContext;
+
+#if CONFIG_GPL
+void ff_tinterlace_init_x86(TInterlaceContext *tinterlace);
+#endif
+
+#define OFFSET(x) offsetof(TInterlaceContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+#define IS_ODD(value) (value & 1)
+
+typedef struct TInterlaceThreadData {
+    AVFrame *out, *first, *second;
+    int plane;
+    TInterlaceContext *tinterlace;
+
+    int scale_w_plane12_factor;
+    int scale_h_plane12_factor;
 
-void ff_tinterlace_init_x86(TInterlaceContext *interlace);
+} TInterlaceThreadData;
 
 #endif /* AVFILTER_TINTERLACE_H */
diff --git a/libavfilter/vf_reinterlace.c b/libavfilter/vf_reinterlace.c
deleted file mode 100644
index d3f8f374ae..0000000000
--- a/libavfilter/vf_reinterlace.c
+++ /dev/null
@@ -1,775 +0,0 @@ 
-/*
- * Copyright (c) 2018 Vasile Toncu <vasile.toncu@tremend.com>
- * Copyright (c) 2017 Thomas Mundt <tmundt75@gmail.com>
- *
- * 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
- * Reinterlace filter
- */
-
-#include "libavutil/pixdesc.h"
-#include "libavutil/imgutils.h"
-#include "drawutils.h"
-#include "reinterlace.h"
-
-static const AVRational standard_tbs[] = {
-    {1, 25},
-    {1, 30},
-    {1001, 30000},
-};
-
-static const AVOption reinterlace_options[] = {
-    { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE},     0, MODE_NB - 1, FLAGS, "mode" },
-    { "merge",             "merge frames",                0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE},                INT_MIN, INT_MAX, FLAGS, "mode"},
-    { "drop_even",         "drop even frames",            0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN},            INT_MIN, INT_MAX, FLAGS, "mode"},
-    { "drop_odd",          "drop odd frames",             0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD},             INT_MIN, INT_MAX, FLAGS, "mode"},
-    { "pad",               "pad lines of a frame with black lines",                  0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD},                  INT_MIN, INT_MAX, FLAGS, "mode"},
-    { "interleave_top",    "interleave top and bottom frames",       0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP},       INT_MIN, INT_MAX, FLAGS, "mode"},
-    { "interleave_bottom", "interleave bottom and top frames",    0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM},    INT_MIN, INT_MAX, FLAGS, "mode"},
-    { "interlacex2",       "interlace consecutive frames",         0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACE_X2},         INT_MIN, INT_MAX, FLAGS, "mode"},
-    { "mergex2",           "just like merge, but at the same frame rate",             0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE_X2},             INT_MIN, INT_MAX, FLAGS, "mode"},
-
-    { "flags", "add flag for reinterlace", OFFSET(flags), AV_OPT_TYPE_INT, {.i64=FLAG_NOTHING}, 0, 0xFF, FLAGS, "flags" },
-    { "low_pass_filter",   "low pass fitler",                 0, AV_OPT_TYPE_CONST, {.i64 = FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags"},
-    { "vlpf",              "low pass filter",                 0, AV_OPT_TYPE_CONST, {.i64 = FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags"},
-    { "complex_filter",    "enable complex vertical low-pass filter",      0, AV_OPT_TYPE_CONST, {.i64 = FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
-    { "cvlpf",             "enable complex vertical low-pass filter",      0, AV_OPT_TYPE_CONST, {.i64 = FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
-    { "exact_tb",          "force a timebase which can represent timestamps exactly", 0, AV_OPT_TYPE_CONST, {.i64 = FLAG_EXACT_TB}, INT_MIN, INT_MAX, FLAGS, "flags" },
-    { NULL }
-};
-
-AVFILTER_DEFINE_CLASS(reinterlace);
-
-static av_cold int init(AVFilterContext *ctx)
-{
-    ReInterlaceContext *reinterlace = ctx->priv;
-    int i;
-
-    for (i = 0; i < 4; i++)
-        reinterlace->black_vec[i] = NULL;
-
-    reinterlace->thread_data = av_malloc(4 * sizeof(ReInterlaceThreadData));
-    if (!reinterlace->thread_data)
-        return AVERROR(ENOMEM);
-
-    return 0;
-}
-
-static int query_formats(AVFilterContext *ctx)
-{
-    static const enum AVPixelFormat pix_fmts[] = {
-        AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
-        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
-        AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
-        AV_PIX_FMT_YUV420P10LE, AV_PIX_FMT_YUV422P10LE,
-        AV_PIX_FMT_YUV440P10LE, AV_PIX_FMT_YUV444P10LE,
-        AV_PIX_FMT_YUV420P12LE, AV_PIX_FMT_YUV422P12LE,
-        AV_PIX_FMT_YUV440P12LE, AV_PIX_FMT_YUV444P12LE,
-        AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
-        AV_PIX_FMT_YUVA420P10LE, AV_PIX_FMT_YUVA422P10LE, AV_PIX_FMT_YUVA444P10LE,
-        AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
-        AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
-        AV_PIX_FMT_NONE
-    };
-
-    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
-
-    if (!fmts_list)
-        return AVERROR(ENOMEM);
-
-    return ff_set_common_formats(ctx, fmts_list);
-}
-
-static void lowpass_line_c(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp,
-                           ptrdiff_t mref, ptrdiff_t pref, int clip_max)
-{
-    const uint8_t *srcp_above = srcp + mref;
-    const uint8_t *srcp_below = srcp + pref;
-    int i;
-    for (i = 0; i < width; i++) {
-        // this calculation is an integer representation of
-        // '0.5 * current + 0.25 * above + 0.25 * below'
-        // '1 +' is for rounding.
-        dstp[i] = (1 + srcp[i] + srcp[i] + srcp_above[i] + srcp_below[i]) >> 2;
-    }
-}
-
-static void lowpass_line_c_16(uint8_t *dst8, ptrdiff_t width, const uint8_t *src8,
-                              ptrdiff_t mref, ptrdiff_t pref, int clip_max)
-{
-    uint16_t *dstp = (uint16_t *)dst8;
-    const uint16_t *srcp = (const uint16_t *)src8;
-    const uint16_t *srcp_above = srcp + mref / 2;
-    const uint16_t *srcp_below = srcp + pref / 2;
-    int i, src_x;
-    for (i = 0; i < width; i++) {
-        // this calculation is an integer representation of
-        // '0.5 * current + 0.25 * above + 0.25 * below'
-        // '1 +' is for rounding.
-        src_x   = av_le2ne16(srcp[i]) << 1;
-        dstp[i] = av_le2ne16((1 + src_x + av_le2ne16(srcp_above[i])
-                             + av_le2ne16(srcp_below[i])) >> 2);
-    }
-}
-
-static void lowpass_line_complex_c(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp,
-                                   ptrdiff_t mref, ptrdiff_t pref, int clip_max)
-{
-    const uint8_t *srcp_above = srcp + mref;
-    const uint8_t *srcp_below = srcp + pref;
-    const uint8_t *srcp_above2 = srcp + mref * 2;
-    const uint8_t *srcp_below2 = srcp + pref * 2;
-    int i, src_x, src_ab;
-    for (i = 0; i < width; i++) {
-        // this calculation is an integer representation of
-        // '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2'
-        // '4 +' is for rounding.
-        src_x   = srcp[i] << 1;
-        src_ab  = srcp_above[i] + srcp_below[i];
-        dstp[i] = av_clip_uint8((4 + ((srcp[i] + src_x + src_ab) << 1)
-                                - srcp_above2[i] - srcp_below2[i]) >> 3);
-        // Prevent over-sharpening:
-        // dst must not exceed src when the average of above and below
-        // is less than src. And the other way around.
-        if (src_ab > src_x) {
-            if (dstp[i] < srcp[i])
-                dstp[i] = srcp[i];
-        } else if (dstp[i] > srcp[i])
-            dstp[i] = srcp[i];
-    }
-}
-
-static void lowpass_line_complex_c_16(uint8_t *dst8, ptrdiff_t width, const uint8_t *src8,
-                                      ptrdiff_t mref, ptrdiff_t pref, int clip_max)
-{
-    uint16_t *dstp = (uint16_t *)dst8;
-    const uint16_t *srcp = (const uint16_t *)src8;
-    const uint16_t *srcp_above = srcp + mref / 2;
-    const uint16_t *srcp_below = srcp + pref / 2;
-    const uint16_t *srcp_above2 = srcp + mref;
-    const uint16_t *srcp_below2 = srcp + pref;
-    int i, dst_le, src_le, src_x, src_ab;
-    for (i = 0; i < width; i++) {
-        // this calculation is an integer representation of
-        // '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2'
-        // '4 +' is for rounding.
-        src_le = av_le2ne16(srcp[i]);
-        src_x  = src_le << 1;
-        src_ab = av_le2ne16(srcp_above[i]) + av_le2ne16(srcp_below[i]);
-        dst_le = av_clip((4 + ((src_le + src_x + src_ab) << 1)
-                         - av_le2ne16(srcp_above2[i])
-                         - av_le2ne16(srcp_below2[i])) >> 3, 0, clip_max);
-        // Prevent over-sharpening:
-        // dst must not exceed src when the average of above and below
-        // is less than src. And the other way around.
-        if (src_ab > src_x) {
-            if (dst_le < src_le)
-                dstp[i] = av_le2ne16(src_le);
-            else
-                dstp[i] = av_le2ne16(dst_le);
-        } else if (dst_le > src_le) {
-            dstp[i] = av_le2ne16(src_le);
-        } else
-            dstp[i] = av_le2ne16(dst_le);
-    }
-}
-
-/**
- * alocate memory for a black frame
- */
-static int init_black_buffers(ReInterlaceContext *reinterlace, AVFilterLink *outlink)
-{
-    FFDrawContext ctx;
-    FFDrawColor color;
-    int ret;
-    int format = outlink->format;
-
-    if ((ret = av_image_alloc(reinterlace->black_vec, reinterlace->black_linesize,
-                         outlink->w, outlink->h, outlink->format, 16)) < 0)
-        return ret;
-
-    ff_draw_init(&ctx, outlink->format, 0);
-    ff_draw_color(&ctx, &color, (uint8_t[]){0, 0, 0, 16});
-    if (format == AV_PIX_FMT_YUVJ420P ||
-        format == AV_PIX_FMT_YUVJ422P ||
-        format == AV_PIX_FMT_YUVJ440P ||
-        format == AV_PIX_FMT_YUVJ444P)
-        color.comp[0].u8[0] = 0;
-    ff_fill_rectangle(&ctx, &color, reinterlace->black_vec,
-                      reinterlace->black_linesize, 0, 0, outlink->w, outlink->h);
-
-    return 0;
-}
-
-static int config_out_props(AVFilterLink *outlink)
-{
-    AVFilterContext *ctx = outlink->src;
-    AVFilterLink *inlink = outlink->src->inputs[0];
-    ReInterlaceContext *reinterlace = ctx->priv;
-    const AVPixFmtDescriptor *fmt_desc = av_pix_fmt_desc_get(outlink->format);
-    int reinterlace_mode = reinterlace->mode;
-    int ret, i;
-
-    reinterlace->bit_depth = fmt_desc->comp[0].depth;
-    reinterlace->preout_time_base = inlink->time_base;
-
-    switch (reinterlace_mode) {
-    case MODE_PAD:
-        outlink->w = inlink->w;
-        outlink->h = 2 * inlink->h;
-        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, av_make_q(2, 1));
-
-        ret = init_black_buffers(reinterlace, outlink);
-        if (ret < 0)
-            return ret;
-        break;
-
-    case MODE_DROP_EVEN:
-    case MODE_DROP_ODD:
-        outlink->w = inlink->w;
-        outlink->h = inlink->h;
-        outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2});
-        outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){2,1});
-        break;
-
-    case MODE_INTERLEAVE_TOP:
-    case MODE_INTERLEAVE_BOTTOM:
-        outlink->w = inlink->w;
-        outlink->h = inlink->h;
-        outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2});
-        outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){2,1});
-        break;
-
-    case MODE_INTERLACE_X2:
-        outlink->w = inlink->w;
-        outlink->h = inlink->h;
-        reinterlace->preout_time_base.den *= 2;
-        outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){2,1});
-        outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){1,2});
-        break;
-
-    case MODE_MERGE_X2:
-        outlink->w = inlink->w;
-        outlink->h = 2 * inlink->h;
-        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, av_make_q(2, 1));
-        outlink->frame_rate = inlink->frame_rate;
-        outlink->time_base  = inlink->time_base;
-        break;
-
-    case MODE_MERGE:
-    case MODE_MERGE_BFF:
-    case MODE_MERGE_TFF:
-        outlink->w = inlink->w;
-        outlink->h = 2 * inlink->h;
-        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, av_make_q(2, 1));
-        outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2});
-        outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){2,1});
-        break;
-
-    default:
-        av_log(ctx, AV_LOG_VERBOSE, "invalid value for mode");
-        av_assert0(0);
-
-    }
-
-    for (i = 0; i < FF_ARRAY_ELEMS(standard_tbs); i++) {
-        if (!av_cmp_q(standard_tbs[i], outlink->time_base))
-            break;
-    }
-    if (i == FF_ARRAY_ELEMS(standard_tbs) || (reinterlace->flags & FLAG_EXACT_TB) )
-        outlink->time_base = reinterlace->preout_time_base;
-
-    if (reinterlace->flags & FLAG_VLPF || reinterlace->flags & FLAG_CVLPF) {
-
-        if (reinterlace_mode != MODE_INTERLEAVE_TOP && reinterlace_mode != MODE_INTERLEAVE_BOTTOM) {
-            reinterlace->flags &= ~(FLAG_VLPF | FLAG_CVLPF);
-        } else {
-            reinterlace->lowpass_line = (reinterlace->flags & FLAG_VLPF) ? lowpass_line_c : lowpass_line_complex_c;
-
-            if (reinterlace->bit_depth > 8) {
-                reinterlace->lowpass_line = (reinterlace->flags & FLAG_VLPF) ? lowpass_line_c_16 : lowpass_line_complex_c_16;
-            }
-
-#if CONFIG_GPL
-            if (ARCH_X86) {
-                ff_reinterlace_init_x86(reinterlace);
-            }
-#endif
-        }
-
-    }
-
-    return 0;
-}
-
-static int filter_frame_plane(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
-    // jobnr is usualy plane number
-    ReInterlaceThreadData *rtd = arg;
-    ReInterlaceContext *reinterlace = rtd->reinterlace;
-    AVFrame *first = rtd->first;
-    AVFrame *second = rtd->second;
-    AVFrame *out = rtd->out;
-
-    int plane = rtd->plane;
-    int reinterlace_mode = reinterlace->mode;
-
-    int x = (plane == 1 || plane == 2) ? rtd->scale_w_plane12_factor : 1;
-    int y = (plane == 1 || plane == 2) ? rtd->scale_h_plane12_factor : 1;
-    int i, ls_offset;
-    int offset1, offset2, offset3, offset4;
-
-    const AVPixFmtDescriptor *fmt_desc = av_pix_fmt_desc_get(out->format);
-    int clip_max = (1 << fmt_desc->comp[plane].depth) - 1;
-    int mult = (fmt_desc->comp[0].depth > 8 ? 2 : 1);
-
-    switch (reinterlace_mode) {
-    case MODE_MERGE:
-        av_image_copy_plane(out->data[plane], 2 * out->linesize[plane],
-            first->data[plane], first->linesize[plane], mult * first->width / x, first->height / y);
-        av_image_copy_plane(out->data[plane] + out->linesize[plane], 2 * out->linesize[plane],
-            second->data[plane], second->linesize[plane], mult * second->width / x, second->height / y);
-        break;
-
-    case MODE_PAD:
-        ls_offset = (reinterlace->current_frame_index & 1) ? 0 : out->linesize[plane];
-        av_image_copy_plane(out->data[plane] + ls_offset, 2 * out->linesize[plane],
-            second->data[plane], second->linesize[plane], mult * second->width / x, second->height / y);
-        av_image_copy_plane(out->data[plane] + out->linesize[plane] - ls_offset, 2 * out->linesize[plane],
-            reinterlace->black_vec[plane], reinterlace->black_linesize[plane], mult * second->width / x, second->height / y);
-        break;
-
-    case MODE_INTERLEAVE_BOTTOM:
-    case MODE_INTERLEAVE_TOP:
-        y = y * 2;
-
-        if (reinterlace->flags & FLAG_VLPF || reinterlace->flags & FLAG_CVLPF) {
-
-            int lines, cols, cvlfp;
-            AVFrame *from_frame;
-            uint8_t *from, *to;
-            int from_step, to_step;
-
-            lines = (reinterlace_mode == MODE_INTERLEAVE_TOP) ? (2 * out->height / y + 1) / 2 : (2 * out->height / y + 0) / 2;
-            cols = out->width / x;
-            from_frame = first;
-            from = from_frame->data[plane];
-            to = out->data[plane];
-
-            if (reinterlace_mode == MODE_INTERLEAVE_BOTTOM) {
-                from = from + from_frame->linesize[plane];
-                to = to + out->linesize[plane];
-            }
-
-            from_step = 2 * from_frame->linesize[plane];
-            to_step = 2 * out->linesize[plane];
-
-            // when i = lines - aka first line
-            reinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
-            to += to_step;
-            from += from_step;
-
-            cvlfp = !!(reinterlace->flags & FLAG_CVLPF);
-            if (cvlfp) {
-                reinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
-                to += to_step;
-                from += from_step;
-            }
-
-            for (i = lines - 2 - 2 * cvlfp; i; i--) {
-                reinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], -from_frame->linesize[plane], clip_max);
-                to += to_step;
-                from += from_step;
-            }
-
-            // when i == 1 - aka last line
-            reinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
-            to += to_step;
-            from += from_step;
-
-            if (cvlfp) {
-                reinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
-                to += to_step;
-                from += from_step;
-            }
-
-
-            lines = (reinterlace_mode == MODE_INTERLEAVE_BOTTOM) ? ((2 * out->height / y) + 1) / 2 : (2 * out->height / y + 0) / 2;
-            cols = out->width / x;
-            from_frame = second;
-            from = from_frame->data[plane];
-            to = out->data[plane];
-
-            if (reinterlace_mode == MODE_INTERLEAVE_TOP) {
-                from = from + from_frame->linesize[plane];
-                to = to + out->linesize[plane];
-            }
-
-            from_step = 2 * from_frame->linesize[plane];
-            to_step = 2 * out->linesize[plane];
-
-            // when i = lines
-            reinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
-            to += to_step;
-            from += from_step;
-
-            if (cvlfp) {
-                reinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
-                to += to_step;
-                from += from_step;
-            }
-
-
-            for (i = lines - 2 - 2 * cvlfp; i; i--) {
-                reinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], -from_frame->linesize[plane], clip_max);
-                to += to_step;
-                from += from_step;
-            }
-
-            // when i == 1
-            reinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
-            to += to_step;
-            from += from_step;
-
-            if (cvlfp) {
-                reinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
-                to += to_step;
-                from += from_step;
-            }
-
-        } else {
-            offset1 = (reinterlace_mode == MODE_INTERLEAVE_TOP) ? 0 : out->linesize[plane];
-            offset2 = (reinterlace_mode == MODE_INTERLEAVE_TOP) ? 0 : first->linesize[plane];
-            offset3 = (reinterlace_mode == MODE_INTERLEAVE_TOP) ? out->linesize[plane]    : 0;
-            offset4 = (reinterlace_mode == MODE_INTERLEAVE_TOP) ? second->linesize[plane] : 0;
-
-            av_image_copy_plane(out->data[plane] + offset1, 2 * out->linesize[plane],
-                first->data[plane] + offset2, 2 * first->linesize[plane],
-                first->width / x, first->height / y);
-            av_image_copy_plane(out->data[plane] + offset3, 2 * out->linesize[plane],
-                second->data[plane] + offset4, 2 * second->linesize[plane],
-                second->width / x, second->height / y);
-        }
-        break;
-
-    case MODE_INTERLACE_X2:
-        y = y * 2;
-
-        offset1 = 0; offset2 = 0;
-        offset3 = out->linesize[plane];
-        offset4 = second->linesize[plane];
-
-        if (second->interlaced_frame && second->top_field_first) {
-            offset1 = out->linesize[plane];
-            offset2 = first->linesize[plane];
-            offset3 = 0; offset4 = 0;
-        }
-
-        av_image_copy_plane(out->data[plane] + offset1, 2 * out->linesize[plane],
-            first->data[plane] + offset2, 2 * first->linesize[plane],
-            first->width / x, first->height / y);
-        av_image_copy_plane(out->data[plane] + offset3, 2 * out->linesize[plane],
-            second->data[plane] + offset4, 2 * second->linesize[plane],
-            second->width / x, second->height / y);
-        break;
-
-    case MODE_MERGE_X2:
-        if (IS_ODD(reinterlace->current_frame_index - 1)) {
-            av_image_copy_plane(out->data[plane], 2 * out->linesize[plane],
-                second->data[plane], second->linesize[plane], second->width / x, second->height / y);
-            av_image_copy_plane(out->data[plane] + out->linesize[plane], 2 * out->linesize[plane],
-                first->data[plane], first->linesize[plane], first->width / x, first->height / y);
-        } else {
-            av_image_copy_plane(out->data[plane], 2 * out->linesize[plane],
-                first->data[plane], first->linesize[plane], first->width / x, first->height / y);
-            av_image_copy_plane(out->data[plane] + out->linesize[plane], 2 * out->linesize[plane],
-                second->data[plane], second->linesize[plane], second->width / x, second->height / y);
-        }
-        break;
-
-    case MODE_MERGE_TFF:
-    case MODE_MERGE_BFF:
-        offset1 = (reinterlace_mode == MODE_MERGE_TFF) ? 0 : out->linesize[plane];
-        offset2 = (reinterlace_mode == MODE_MERGE_TFF) ? out->linesize[plane] : 0;
-
-        av_image_copy_plane(out->data[plane] + offset1, 2 * out->linesize[plane],
-            first->data[plane], first->linesize[plane], first->width / x, first->height / y);
-        av_image_copy_plane(out->data[plane] + offset2, 2 * out->linesize[plane],
-            second->data[plane], second->linesize[plane], second->width / x, second->height / y);
-        break;
-
-    default:
-        break;
-    }
-
-    return 0;
-}
-
-static ReInterlaceThreadData *get_ReInterlaceThreadData(AVFrame *out, AVFrame *first, AVFrame *second,
-                int plane, ReInterlaceContext *reinterlace,
-                int scale_w_plane12_factor,
-                int scale_h_plane12_factor)
-{
-    ReInterlaceThreadData *rtd = &((ReInterlaceThreadData *)reinterlace->thread_data)[plane];
-
-    if (!rtd)
-        return rtd;
-
-    rtd->out = out;
-    rtd->first = first;
-    rtd->second = second;
-    rtd->plane = plane;
-    rtd->reinterlace = reinterlace;
-    rtd->scale_h_plane12_factor = scale_h_plane12_factor;
-    rtd->scale_w_plane12_factor = scale_w_plane12_factor;
-
-    return rtd;
-}
-
-static void copy_all_planes(AVFilterContext *ctx,
-    ReInterlaceContext *reinterlace,
-    const AVPixFmtDescriptor *desc,
-    AVFrame *out, AVFrame *first, AVFrame *second)
-{
-    int scale_w_plane12_factor = 1 << desc->log2_chroma_w;
-    int scale_h_plane12_factor = 1 << desc->log2_chroma_h;
-    int plane;
-
-    for (plane = 0; plane < desc->nb_components; plane++) {
-
-        ReInterlaceThreadData *rtd = get_ReInterlaceThreadData(out, first, second,
-            plane, reinterlace, scale_w_plane12_factor, scale_h_plane12_factor);
-
-        ctx->internal->execute(ctx, filter_frame_plane, rtd, NULL, FFMIN(desc->nb_components, ctx->graph->nb_threads));
-    }
-}
-
-static int filter_frame(AVFilterLink *inlink, AVFrame *in)
-{
-    AVFilterContext *ctx            = inlink->dst;
-    ReInterlaceContext *reinterlace = ctx->priv;
-    AVFilterLink *outlink           = ctx->outputs[0];
-    const AVPixFmtDescriptor *desc  = av_pix_fmt_desc_get(outlink->format);
-    AVFrame *out, *first, *second;
-    int ret;
-
-    int reinterlace_mode = reinterlace->mode;
-
-    av_frame_free(&(reinterlace->prev_frame));
-    reinterlace->prev_frame = reinterlace->current_frame;
-    reinterlace->current_frame = in;
-    reinterlace->current_frame_index++;
-
-    // we process two frames at a time, thus only even frame indexes are considered
-    if (IS_ODD(reinterlace->current_frame_index)) {
-        if (reinterlace_mode == MODE_PAD || reinterlace_mode == MODE_MERGE_X2
-            || reinterlace_mode == MODE_INTERLACE_X2 || reinterlace_mode == MODE_MERGE_BFF
-            || reinterlace_mode == MODE_MERGE_TFF) {
-            // continue
-        } else {
-            return 0;
-        }
-    }
-
-    first   = reinterlace->prev_frame;
-    second  = reinterlace->current_frame;
-
-    switch (reinterlace_mode) {
-    case MODE_DROP_EVEN:
-    case MODE_DROP_ODD:
-        out = (reinterlace_mode == MODE_DROP_ODD) ? reinterlace->current_frame : reinterlace->prev_frame;
-        out = av_frame_clone(out);
-
-        if (!out)
-            return AVERROR(ENOMEM);
-
-        out->pts = av_rescale_q(out->pts, reinterlace->preout_time_base, outlink->time_base);
-        ret = ff_filter_frame(outlink, out);
-        break;
-
-    case MODE_MERGE:
-    case MODE_MERGE_X2:
-    case MODE_MERGE_TFF:
-    case MODE_MERGE_BFF:
-        if (reinterlace_mode == MODE_MERGE_X2 && reinterlace->current_frame_index == 1)
-            return 0;
-
-        if (reinterlace_mode == MODE_MERGE_BFF || reinterlace_mode == MODE_MERGE_TFF) {
-           if (!first)
-                return 0;
-
-            if (reinterlace->skip_next_frame) {
-                reinterlace->skip_next_frame = 0;
-                return 0;
-            }
-
-            if (first->interlaced_frame == 1 && second->interlaced_frame == 1)
-            {
-                if (first->top_field_first == second->top_field_first)
-                    return 0;
-                else if (reinterlace->mode == MODE_MERGE_BFF && first->top_field_first != 0)
-                    return 0;
-                else if (reinterlace->mode == MODE_MERGE_TFF && first->top_field_first != 1)
-                    return 0;
-            }
-        }
-
-        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
-
-        if (!out)
-            return AVERROR(ENOMEM);
-
-        av_frame_copy_props(out, first);
-        out->sample_aspect_ratio    = av_mul_q(first->sample_aspect_ratio, av_make_q(2, 1));
-        out->interlaced_frame       = 1;
-        out->top_field_first        = reinterlace_mode == MODE_MERGE_BFF ? 0 : 1;
-        out->height                 = outlink->h;
-
-        copy_all_planes(ctx, reinterlace, desc, out, first, second);
-
-        if (reinterlace_mode == MODE_MERGE_BFF || reinterlace_mode == MODE_MERGE_TFF)
-            reinterlace->skip_next_frame = 1;
-
-        out->pts = av_rescale_q(out->pts, reinterlace->preout_time_base, outlink->time_base);
-        ret = ff_filter_frame(outlink, out);
-        break;
-
-    case MODE_PAD:
-        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
-
-        if (!out)
-            return AVERROR(ENOMEM);
-
-        av_frame_copy_props(out, second);
-        out->sample_aspect_ratio = av_mul_q(second->sample_aspect_ratio, av_make_q(2, 1));
-        out->height = outlink->h;
-
-        copy_all_planes(ctx, reinterlace, desc, out, first, second);
-
-        out->pts = av_rescale_q(out->pts, reinterlace->preout_time_base, outlink->time_base);
-        ret = ff_filter_frame(outlink, out);
-        break;
-
-    case MODE_INTERLEAVE_BOTTOM:
-    case MODE_INTERLEAVE_TOP:
-        out  = ff_get_video_buffer(outlink, outlink->w, outlink->h);
-
-        if (!out)
-            return AVERROR(ENOMEM);
-
-        av_frame_copy_props(out, first);
-
-        copy_all_planes(ctx, reinterlace, desc, out, first, second);
-
-        out->interlaced_frame = 1;
-        out->top_field_first = (reinterlace_mode == MODE_INTERLEAVE_TOP) ? 1 : 0;
-
-        out->pts = av_rescale_q(out->pts, reinterlace->preout_time_base, outlink->time_base);
-        ret = ff_filter_frame(outlink, out);
-        break;
-
-    case MODE_INTERLACE_X2:
-        if (reinterlace->current_frame_index == 1)
-            return 0;
-
-        out = av_frame_clone(first);
-
-        if (!out)
-            return AVERROR(ENOMEM);
-
-        // output first frame
-        out->pts = (AV_NOPTS_VALUE != first->pts ) ? first->pts * 2 : AV_NOPTS_VALUE;
-        out->interlaced_frame = 1;
-        out->pts = av_rescale_q(out->pts, reinterlace->preout_time_base, outlink->time_base);
-        ret = ff_filter_frame(outlink, out);
-
-        if (ret < 0)
-            return ret;
-
-        // output the second frame interlaced with first frame
-        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
-
-        if (!out)
-            return AVERROR(ENOMEM);
-
-        av_frame_copy_props(out, second);
-        out->interlaced_frame = 1;
-        out->top_field_first = !out->top_field_first;
-        out->pts = first->pts + second->pts;
-        out->pts = ( first->pts == AV_NOPTS_VALUE || second->pts == AV_NOPTS_VALUE) ? AV_NOPTS_VALUE : out->pts;
-
-        copy_all_planes(ctx, reinterlace, desc, out, first, second);
-
-        out->pts = av_rescale_q(out->pts, reinterlace->preout_time_base, outlink->time_base);
-        ret = ff_filter_frame(outlink, out);
-        break;
-
-    default:
-        av_assert0(0);
-    }
-
-    return ret;
-}
-
-static av_cold void uninit(AVFilterContext *ctx)
-{
-    ReInterlaceContext *reinterlace = ctx->priv;
-
-    av_frame_free(&reinterlace->prev_frame);
-    av_frame_free(&reinterlace->current_frame);
-
-    if (reinterlace->black_vec[0] != NULL)
-    {
-        av_freep(&reinterlace->black_vec[0]);
-    }
-
-    av_free(reinterlace->thread_data);
-}
-
-static const AVFilterPad reinterlace_inputs[] = {
-    {
-        .name         = "default",
-        .type         = AVMEDIA_TYPE_VIDEO,
-        .filter_frame = filter_frame,
-    },
-    { NULL }
-};
-
-static const AVFilterPad reinterlace_outputs[] = {
-    {
-        .name = "default",
-        .type = AVMEDIA_TYPE_VIDEO,
-        .config_props = config_out_props,
-    },
-    { NULL }
-};
-
-AVFilter ff_vf_reinterlace = {
-    .name          = "reinterlace",
-    .description   = NULL_IF_CONFIG_SMALL("Various interlace frame manipulations"),
-    .priv_size     = sizeof(ReInterlaceContext),
-    .init          = init,
-    .uninit        = uninit,
-    .query_formats = query_formats,
-    .inputs        = reinterlace_inputs,
-    .outputs       = reinterlace_outputs,
-    .priv_class    = &reinterlace_class,
-    .flags         = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
-};
diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c
index 4f5c10d7ac..6182e29ff7 100644
--- a/libavfilter/vf_tinterlace.c
+++ b/libavfilter/vf_tinterlace.c
@@ -1,60 +1,58 @@ 
 /*
+ * Copyright (c) 2018 Vasile Toncu <vasile.toncu@tremend.com>
  * Copyright (c) 2017 Thomas Mundt <tmundt75@gmail.com>
- * Copyright (c) 2011 Stefano Sabatini
- * Copyright (c) 2010 Baptiste Coudurier
- * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com>
  *
  * This file is part of FFmpeg.
  *
- * FFmpeg is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * 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 General Public License for more details.
+ * 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 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.
+ * 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
- * temporal field interlace filter, ported from MPlayer/libmpcodecs
+ * TInterlace filter
  */
 
-#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
 #include "libavutil/imgutils.h"
-#include "libavutil/avassert.h"
-#include "avfilter.h"
-#include "internal.h"
+#include "drawutils.h"
 #include "tinterlace.h"
 
-#define OFFSET(x) offsetof(TInterlaceContext, x)
-#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+static const AVRational standard_tbs[] = {
+    {1, 25},
+    {1, 30},
+    {1001, 30000},
+};
 
 static const AVOption tinterlace_options[] = {
-    {"mode",              "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, "mode"},
-    {"merge",             "merge fields",                                 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE},             INT_MIN, INT_MAX, FLAGS, "mode"},
-    {"drop_even",         "drop even fields",                             0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN},         INT_MIN, INT_MAX, FLAGS, "mode"},
-    {"drop_odd",          "drop odd fields",                              0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD},          INT_MIN, INT_MAX, FLAGS, "mode"},
-    {"pad",               "pad alternate lines with black",               0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD},               INT_MIN, INT_MAX, FLAGS, "mode"},
-    {"interleave_top",    "interleave top and bottom fields",             0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP},    INT_MIN, INT_MAX, FLAGS, "mode"},
-    {"interleave_bottom", "interleave bottom and top fields",             0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "mode"},
-    {"interlacex2",       "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2},       INT_MIN, INT_MAX, FLAGS, "mode"},
-    {"mergex2",           "merge fields keeping same frame rate",         0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGEX2},           INT_MIN, INT_MAX, FLAGS, "mode"},
-
-    {"flags",             "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, 0, "flags" },
-    {"low_pass_filter",   "enable vertical low-pass filter",              0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" },
-    {"vlpf",              "enable vertical low-pass filter",              0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" },
-    {"complex_filter",    "enable complex vertical low-pass filter",      0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
-    {"cvlpf",             "enable complex vertical low-pass filter",      0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
-    {"exact_tb",          "force a timebase which can represent timestamps exactly", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_EXACT_TB}, INT_MIN, INT_MAX, FLAGS, "flags" },
-
-    {NULL}
+    { "mode",               "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE},     0, MODE_NB - 1, FLAGS, "mode" },
+    { "merge",              "merge frames",                                 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE},                INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "drop_even",          "drop even frames",                             0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN},            INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "drop_odd",           "drop odd frames",                              0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD},             INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "pad",                "pad lines of a frame with black lines",        0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD},                  INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "interleave_top",     "interleave top and bottom frames",             0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP},       INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "interleave_bottom",  "interleave bottom and top frames",             0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM},    INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "interlacex2",        "interlace consecutive frames",                 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACE_X2},         INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "mergex2",            "just like merge, but at the same frame rate",  0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE_X2},             INT_MIN, INT_MAX, FLAGS, "mode"},
+
+    { "flags",              "add flag for tinterlace", OFFSET(flags), AV_OPT_TYPE_INT, {.i64=FLAG_NOTHING}, 0, 0xFF, FLAGS, "flags" },
+    { "low_pass_filter",    "low pass fitler",                              0, AV_OPT_TYPE_CONST, {.i64 = FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags"},
+    { "vlpf",               "low pass filter",                              0, AV_OPT_TYPE_CONST, {.i64 = FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags"},
+    { "complex_filter",     "enable complex vertical low-pass filter",      0, AV_OPT_TYPE_CONST, {.i64 = FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
+    { "cvlpf",              "enable complex vertical low-pass filter",      0, AV_OPT_TYPE_CONST, {.i64 = FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
+    { "exact_tb",           "force a timebase which can represent timestamps exactly", 0, AV_OPT_TYPE_CONST, {.i64 = FLAG_EXACT_TB}, INT_MIN, INT_MAX, FLAGS, "flags" },
+    { NULL }
 };
 
 AVFILTER_DEFINE_CLASS(tinterlace);
@@ -63,28 +61,30 @@  static const AVOption interlace_options[] = {
    { "scan",              "scanning mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_INTERLEAVE_TOP}, 0, MODE_NB-1, FLAGS, "mode"},
    { "tff",               "top field first",                              0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP},    INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
    { "bff",               "bottom field first",                           0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"},
-   { "lowpass",           "set vertical low-pass filter", OFFSET(flags), AV_OPT_TYPE_FLAGS,   {.i64 = TINTERLACE_FLAG_VLPF}, 0,INT_MAX, 0, "flags" },
+   { "lowpass",           "set vertical low-pass filter", OFFSET(flags), AV_OPT_TYPE_FLAGS,   {.i64 = FLAG_VLPF}, 0,INT_MAX, 0, "flags" },
    { "off",               "disable vertical low-pass filter",             0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, FLAGS, "flags" },
-   { "linear",            "linear vertical low-pass filter",              0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" },
-   { "complex",           "complex vertical low-pass filter",             0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
+   { "linear",            "linear vertical low-pass filter",              0, AV_OPT_TYPE_CONST, {.i64 = FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" },
+   { "complex",           "complex vertical low-pass filter",             0, AV_OPT_TYPE_CONST, {.i64 = FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" },
 
    { NULL }
 };
 
 AVFILTER_DEFINE_CLASS(interlace);
 
-#define FULL_SCALE_YUVJ_FORMATS \
-    AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P
+static av_cold int init(AVFilterContext *ctx)
+{
+    TInterlaceContext *tinterlace = ctx->priv;
+    int i;
 
-static const enum AVPixelFormat full_scale_yuvj_pix_fmts[] = {
-    FULL_SCALE_YUVJ_FORMATS, AV_PIX_FMT_NONE
-};
+    for (i = 0; i < 4; i++)
+        tinterlace->black_vec[i] = NULL;
 
-static const AVRational standard_tbs[] = {
-    {1, 25},
-    {1, 30},
-    {1001, 30000},
-};
+    tinterlace->thread_data = av_malloc(4 * sizeof(TInterlaceThreadData));
+    if (!tinterlace->thread_data)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
 
 static int query_formats(AVFilterContext *ctx)
 {
@@ -98,13 +98,16 @@  static int query_formats(AVFilterContext *ctx)
         AV_PIX_FMT_YUV440P12LE, AV_PIX_FMT_YUV444P12LE,
         AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
         AV_PIX_FMT_YUVA420P10LE, AV_PIX_FMT_YUVA422P10LE, AV_PIX_FMT_YUVA444P10LE,
-        AV_PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS,
+        AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
+        AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
         AV_PIX_FMT_NONE
     };
 
     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
+
     if (!fmts_list)
         return AVERROR(ENOMEM);
+
     return ff_set_common_formats(ctx, fmts_list);
 }
 
@@ -202,315 +205,558 @@  static void lowpass_line_complex_c_16(uint8_t *dst8, ptrdiff_t width, const uint
     }
 }
 
-static av_cold void uninit(AVFilterContext *ctx)
+/**
+ * alocate memory for a black frame
+ */
+static int init_black_buffers(TInterlaceContext *tinterlace, AVFilterLink *outlink)
 {
-    TInterlaceContext *tinterlace = ctx->priv;
+    FFDrawContext ctx;
+    FFDrawColor color;
+    int ret;
+    int format = outlink->format;
+
+    if ((ret = av_image_alloc(tinterlace->black_vec, tinterlace->black_linesize,
+                         outlink->w, outlink->h, format, 16)) < 0)
+        return ret;
+
+    ff_draw_init(&ctx, outlink->format, 0);
+    ff_draw_color(&ctx, &color, (uint8_t[]){0, 0, 0, 16});
+    if (format == AV_PIX_FMT_YUVJ420P ||
+        format == AV_PIX_FMT_YUVJ422P ||
+        format == AV_PIX_FMT_YUVJ440P ||
+        format == AV_PIX_FMT_YUVJ444P)
+        color.comp[0].u8[0] = 0;
+    ff_fill_rectangle(&ctx, &color, tinterlace->black_vec,
+                      tinterlace->black_linesize, 0, 0, outlink->w, outlink->h);
 
-    av_frame_free(&tinterlace->cur );
-    av_frame_free(&tinterlace->next);
-    av_freep(&tinterlace->black_data[0]);
+    return 0;
 }
 
 static int config_out_props(AVFilterLink *outlink)
 {
     AVFilterContext *ctx = outlink->src;
     AVFilterLink *inlink = outlink->src->inputs[0];
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
     TInterlaceContext *tinterlace = ctx->priv;
-    int i;
+    const AVPixFmtDescriptor *fmt_desc = av_pix_fmt_desc_get(outlink->format);
+    int tinterlace_mode = tinterlace->mode;
+    int ret, i;
+
+    tinterlace->bit_depth = fmt_desc->comp[0].depth;
+    tinterlace->preout_time_base = inlink->time_base;
+
+    switch (tinterlace_mode) {
+    case MODE_PAD:
+        outlink->w = inlink->w;
+        outlink->h = 2 * inlink->h;
+        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, av_make_q(2, 1));
 
-    tinterlace->vsub = desc->log2_chroma_h;
-    outlink->w = inlink->w;
-    outlink->h = tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD || tinterlace->mode == MODE_MERGEX2?
-        inlink->h*2 : inlink->h;
-    if (tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD || tinterlace->mode == MODE_MERGEX2)
-        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio,
-                                                av_make_q(2, 1));
-
-    if (tinterlace->mode == MODE_PAD) {
-        uint8_t black[4] = { 0, 0, 0, 16 };
-        int ret;
-        ff_draw_init(&tinterlace->draw, outlink->format, 0);
-        ff_draw_color(&tinterlace->draw, &tinterlace->color, black);
-        if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts))
-            tinterlace->color.comp[0].u8[0] = 0;
-        ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize,
-                             outlink->w, outlink->h, outlink->format, 16);
+        ret = init_black_buffers(tinterlace, outlink);
         if (ret < 0)
             return ret;
+        break;
 
-        ff_fill_rectangle(&tinterlace->draw, &tinterlace->color, tinterlace->black_data,
-                          tinterlace->black_linesize, 0, 0, outlink->w, outlink->h);
-    }
-    if (tinterlace->flags & (TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF)
-            && !(tinterlace->mode == MODE_INTERLEAVE_TOP
-              || tinterlace->mode == MODE_INTERLEAVE_BOTTOM)) {
-        av_log(ctx, AV_LOG_WARNING, "low_pass_filter flags ignored with mode %d\n",
-                tinterlace->mode);
-        tinterlace->flags &= ~(TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF);
-    }
-    tinterlace->preout_time_base = inlink->time_base;
-    if (tinterlace->mode == MODE_INTERLACEX2) {
+    case MODE_DROP_EVEN:
+    case MODE_DROP_ODD:
+        outlink->w = inlink->w;
+        outlink->h = inlink->h;
+        outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2});
+        outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){2,1});
+        break;
+
+    case MODE_INTERLEAVE_TOP:
+    case MODE_INTERLEAVE_BOTTOM:
+        outlink->w = inlink->w;
+        outlink->h = inlink->h;
+        outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2});
+        outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){2,1});
+        break;
+
+    case MODE_INTERLACE_X2:
+        outlink->w = inlink->w;
+        outlink->h = inlink->h;
         tinterlace->preout_time_base.den *= 2;
         outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){2,1});
         outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){1,2});
-    } else if (tinterlace->mode == MODE_MERGEX2) {
+        break;
+
+    case MODE_MERGE_X2:
+        outlink->w = inlink->w;
+        outlink->h = 2 * inlink->h;
+        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, av_make_q(2, 1));
         outlink->frame_rate = inlink->frame_rate;
         outlink->time_base  = inlink->time_base;
-    } else if (tinterlace->mode != MODE_PAD) {
+        break;
+
+    case MODE_MERGE:
+    case MODE_MERGE_BFF:
+    case MODE_MERGE_TFF:
+        outlink->w = inlink->w;
+        outlink->h = 2 * inlink->h;
+        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, av_make_q(2, 1));
         outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2});
         outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){2,1});
+        break;
+
+    default:
+        av_log(ctx, AV_LOG_VERBOSE, "invalid value for mode");
+        av_assert0(0);
+
     }
 
-    for (i = 0; i<FF_ARRAY_ELEMS(standard_tbs); i++){
+    for (i = 0; i < FF_ARRAY_ELEMS(standard_tbs); i++) {
         if (!av_cmp_q(standard_tbs[i], outlink->time_base))
             break;
     }
-    if (i == FF_ARRAY_ELEMS(standard_tbs) ||
-        (tinterlace->flags & TINTERLACE_FLAG_EXACT_TB))
+    if (i == FF_ARRAY_ELEMS(standard_tbs) || (tinterlace->flags & FLAG_EXACT_TB) )
         outlink->time_base = tinterlace->preout_time_base;
 
-    tinterlace->csp = av_pix_fmt_desc_get(outlink->format);
-    if (tinterlace->flags & TINTERLACE_FLAG_CVLPF) {
-        if (tinterlace->csp->comp[0].depth > 8)
-            tinterlace->lowpass_line = lowpass_line_complex_c_16;
-        else
-            tinterlace->lowpass_line = lowpass_line_complex_c;
-        if (ARCH_X86)
-            ff_tinterlace_init_x86(tinterlace);
-    } else if (tinterlace->flags & TINTERLACE_FLAG_VLPF) {
-        if (tinterlace->csp->comp[0].depth > 8)
-            tinterlace->lowpass_line = lowpass_line_c_16;
-        else
-            tinterlace->lowpass_line = lowpass_line_c;
-        if (ARCH_X86)
-            ff_tinterlace_init_x86(tinterlace);
-    }
+    if (tinterlace->flags & FLAG_VLPF || tinterlace->flags & FLAG_CVLPF) {
+
+        if (tinterlace_mode != MODE_INTERLEAVE_TOP && tinterlace_mode != MODE_INTERLEAVE_BOTTOM) {
+            tinterlace->flags &= ~(FLAG_VLPF | FLAG_CVLPF);
+        } else {
+            tinterlace->lowpass_line = (tinterlace->flags & FLAG_VLPF) ? lowpass_line_c : lowpass_line_complex_c;
+
+            if (tinterlace->bit_depth > 8) {
+                tinterlace->lowpass_line = (tinterlace->flags & FLAG_VLPF) ? lowpass_line_c_16 : lowpass_line_complex_c_16;
+            }
+
+#if CONFIG_GPL
+            if (ARCH_X86) {
+                ff_tinterlace_init_x86(tinterlace);
+            }
+#endif
+        }
 
-    av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", tinterlace->mode,
-           (tinterlace->flags & TINTERLACE_FLAG_CVLPF) ? "complex" :
-           (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "linear" : "off",
-           inlink->h, outlink->h);
+    }
 
     return 0;
 }
 
-#define FIELD_UPPER           0
-#define FIELD_LOWER           1
-#define FIELD_UPPER_AND_LOWER 2
-
-/**
- * Copy picture field from src to dst.
- *
- * @param src_field copy from upper, lower field or both
- * @param interleave leave a padding line between each copied line
- * @param dst_field copy to upper or lower field,
- *        only meaningful when interleave is selected
- * @param flags context flags
- */
-static inline
-void copy_picture_field(TInterlaceContext *tinterlace,
-                        uint8_t *dst[4], int dst_linesize[4],
-                        const uint8_t *src[4], int src_linesize[4],
-                        enum AVPixelFormat format, int w, int src_h,
-                        int src_field, int interleave, int dst_field,
-                        int flags)
+static int filter_frame_plane(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
 {
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format);
-    int hsub = desc->log2_chroma_w;
-    int plane, vsub = desc->log2_chroma_h;
-    int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2;
-    int h;
+    // jobnr is usualy plane number
+    TInterlaceThreadData *rtd = arg;
+    TInterlaceContext *tinterlace = rtd->tinterlace;
+    AVFrame *first = rtd->first;
+    AVFrame *second = rtd->second;
+    AVFrame *out = rtd->out;
+
+    int plane = rtd->plane;
+    int tinterlace_mode = tinterlace->mode;
+
+    int x = (plane == 1 || plane == 2) ? rtd->scale_w_plane12_factor : 1;
+    int y = (plane == 1 || plane == 2) ? rtd->scale_h_plane12_factor : 1;
+    int i, ls_offset;
+    int offset1, offset2, offset3, offset4;
+
+    const AVPixFmtDescriptor *fmt_desc = av_pix_fmt_desc_get(out->format);
+    int clip_max = (1 << fmt_desc->comp[plane].depth) - 1;
+    int mult = (fmt_desc->comp[0].depth > 8 ? 2 : 1);
+
+    switch (tinterlace_mode) {
+    case MODE_MERGE:
+        av_image_copy_plane(out->data[plane], 2 * out->linesize[plane],
+            first->data[plane], first->linesize[plane], mult * first->width / x, first->height / y);
+        av_image_copy_plane(out->data[plane] + out->linesize[plane], 2 * out->linesize[plane],
+            second->data[plane], second->linesize[plane], mult * second->width / x, second->height / y);
+        break;
 
-    for (plane = 0; plane < desc->nb_components; plane++) {
-        int lines = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(src_h, vsub) : src_h;
-        int cols  = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(    w, hsub) : w;
-        uint8_t *dstp = dst[plane];
-        const uint8_t *srcp = src[plane];
-        int srcp_linesize = src_linesize[plane] * k;
-        int dstp_linesize = dst_linesize[plane] * (interleave ? 2 : 1);
-        int clip_max = (1 << tinterlace->csp->comp[plane].depth) - 1;
-
-        lines = (lines + (src_field == FIELD_UPPER)) / k;
-        if (src_field == FIELD_LOWER)
-            srcp += src_linesize[plane];
-        if (interleave && dst_field == FIELD_LOWER)
-            dstp += dst_linesize[plane];
-        // Low-pass filtering is required when creating an interlaced destination from
-        // a progressive source which contains high-frequency vertical detail.
-        // Filtering will reduce interlace 'twitter' and Moire patterning.
-        if (flags & (TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF)) {
-            int x = !!(flags & TINTERLACE_FLAG_CVLPF);
-            for (h = lines; h > 0; h--) {
-                ptrdiff_t pref = src_linesize[plane];
-                ptrdiff_t mref = -pref;
-                if (h >= (lines - x))  mref = 0; // there is no line above
-                else if (h <= (1 + x)) pref = 0; // there is no line below
-
-                tinterlace->lowpass_line(dstp, cols, srcp, mref, pref, clip_max);
-                dstp += dstp_linesize;
-                srcp += srcp_linesize;
+    case MODE_PAD:
+        ls_offset = (tinterlace->current_frame_index & 1) ? 0 : out->linesize[plane];
+        av_image_copy_plane(out->data[plane] + ls_offset, 2 * out->linesize[plane],
+            second->data[plane], second->linesize[plane], mult * second->width / x, second->height / y);
+        av_image_copy_plane(out->data[plane] + out->linesize[plane] - ls_offset, 2 * out->linesize[plane],
+            tinterlace->black_vec[plane], tinterlace->black_linesize[plane], mult * second->width / x, second->height / y);
+        break;
+
+    case MODE_INTERLEAVE_BOTTOM:
+    case MODE_INTERLEAVE_TOP:
+        y = y * 2;
+
+        if (tinterlace->flags & FLAG_VLPF || tinterlace->flags & FLAG_CVLPF) {
+
+            int lines, cols, cvlfp;
+            AVFrame *from_frame;
+            uint8_t *from, *to;
+            int from_step, to_step;
+
+            lines = (tinterlace_mode == MODE_INTERLEAVE_TOP) ? (2 * out->height / y + 1) / 2 : (2 * out->height / y + 0) / 2;
+            cols = out->width / x;
+            from_frame = first;
+            from = from_frame->data[plane];
+            to = out->data[plane];
+
+            if (tinterlace_mode == MODE_INTERLEAVE_BOTTOM) {
+                from = from + from_frame->linesize[plane];
+                to = to + out->linesize[plane];
+            }
+
+            from_step = 2 * from_frame->linesize[plane];
+            to_step = 2 * out->linesize[plane];
+
+            // when i = lines - aka first line
+            tinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
+            to += to_step;
+            from += from_step;
+
+            cvlfp = !!(tinterlace->flags & FLAG_CVLPF);
+            if (cvlfp) {
+                tinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
+                to += to_step;
+                from += from_step;
+            }
+
+            for (i = lines - 2 - 2 * cvlfp; i; i--) {
+                tinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], -from_frame->linesize[plane], clip_max);
+                to += to_step;
+                from += from_step;
+            }
+
+            // when i == 1 - aka last line
+            tinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
+            to += to_step;
+            from += from_step;
+
+            if (cvlfp) {
+                tinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
+                to += to_step;
+                from += from_step;
+            }
+
+
+            lines = (tinterlace_mode == MODE_INTERLEAVE_BOTTOM) ? ((2 * out->height / y) + 1) / 2 : (2 * out->height / y + 0) / 2;
+            cols = out->width / x;
+            from_frame = second;
+            from = from_frame->data[plane];
+            to = out->data[plane];
+
+            if (tinterlace_mode == MODE_INTERLEAVE_TOP) {
+                from = from + from_frame->linesize[plane];
+                to = to + out->linesize[plane];
             }
+
+            from_step = 2 * from_frame->linesize[plane];
+            to_step = 2 * out->linesize[plane];
+
+            // when i = lines
+            tinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
+            to += to_step;
+            from += from_step;
+
+            if (cvlfp) {
+                tinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], 0, clip_max);
+                to += to_step;
+                from += from_step;
+            }
+
+
+            for (i = lines - 2 - 2 * cvlfp; i; i--) {
+                tinterlace->lowpass_line(to, cols, from, from_frame->linesize[plane], -from_frame->linesize[plane], clip_max);
+                to += to_step;
+                from += from_step;
+            }
+
+            // when i == 1
+            tinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
+            to += to_step;
+            from += from_step;
+
+            if (cvlfp) {
+                tinterlace->lowpass_line(to, cols, from, 0, -from_frame->linesize[plane], clip_max);
+                to += to_step;
+                from += from_step;
+            }
+
+        } else {
+            offset1 = (tinterlace_mode == MODE_INTERLEAVE_TOP) ? 0 : out->linesize[plane];
+            offset2 = (tinterlace_mode == MODE_INTERLEAVE_TOP) ? 0 : first->linesize[plane];
+            offset3 = (tinterlace_mode == MODE_INTERLEAVE_TOP) ? out->linesize[plane]    : 0;
+            offset4 = (tinterlace_mode == MODE_INTERLEAVE_TOP) ? second->linesize[plane] : 0;
+
+            av_image_copy_plane(out->data[plane] + offset1, 2 * out->linesize[plane],
+                first->data[plane] + offset2, 2 * first->linesize[plane],
+                first->width / x, first->height / y);
+            av_image_copy_plane(out->data[plane] + offset3, 2 * out->linesize[plane],
+                second->data[plane] + offset4, 2 * second->linesize[plane],
+                second->width / x, second->height / y);
+        }
+        break;
+
+    case MODE_INTERLACE_X2:
+        y = y * 2;
+
+        offset1 = 0; offset2 = 0;
+        offset3 = out->linesize[plane];
+        offset4 = second->linesize[plane];
+
+        if (second->interlaced_frame && second->top_field_first) {
+            offset1 = out->linesize[plane];
+            offset2 = first->linesize[plane];
+            offset3 = 0; offset4 = 0;
+        }
+
+        av_image_copy_plane(out->data[plane] + offset1, 2 * out->linesize[plane],
+            first->data[plane] + offset2, 2 * first->linesize[plane],
+            first->width / x, first->height / y);
+        av_image_copy_plane(out->data[plane] + offset3, 2 * out->linesize[plane],
+            second->data[plane] + offset4, 2 * second->linesize[plane],
+            second->width / x, second->height / y);
+        break;
+
+    case MODE_MERGE_X2:
+        if (IS_ODD(tinterlace->current_frame_index - 1)) {
+            av_image_copy_plane(out->data[plane], 2 * out->linesize[plane],
+                second->data[plane], second->linesize[plane], second->width / x, second->height / y);
+            av_image_copy_plane(out->data[plane] + out->linesize[plane], 2 * out->linesize[plane],
+                first->data[plane], first->linesize[plane], first->width / x, first->height / y);
         } else {
-            if (tinterlace->csp->comp[plane].depth > 8)
-                cols *= 2;
-            av_image_copy_plane(dstp, dstp_linesize, srcp, srcp_linesize, cols, lines);
+            av_image_copy_plane(out->data[plane], 2 * out->linesize[plane],
+                first->data[plane], first->linesize[plane], first->width / x, first->height / y);
+            av_image_copy_plane(out->data[plane] + out->linesize[plane], 2 * out->linesize[plane],
+                second->data[plane], second->linesize[plane], second->width / x, second->height / y);
         }
+        break;
+
+    case MODE_MERGE_TFF:
+    case MODE_MERGE_BFF:
+        offset1 = (tinterlace_mode == MODE_MERGE_TFF) ? 0 : out->linesize[plane];
+        offset2 = (tinterlace_mode == MODE_MERGE_TFF) ? out->linesize[plane] : 0;
+
+        av_image_copy_plane(out->data[plane] + offset1, 2 * out->linesize[plane],
+            first->data[plane], first->linesize[plane], first->width / x, first->height / y);
+        av_image_copy_plane(out->data[plane] + offset2, 2 * out->linesize[plane],
+            second->data[plane], second->linesize[plane], second->width / x, second->height / y);
+        break;
+
+    default:
+        break;
     }
+
+    return 0;
 }
 
-static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
+static TInterlaceThreadData *get_TInterlaceThreadData(AVFrame *out, AVFrame *first, AVFrame *second,
+                int plane, TInterlaceContext *tinterlace,
+                int scale_w_plane12_factor,
+                int scale_h_plane12_factor)
 {
-    AVFilterContext *ctx = inlink->dst;
-    AVFilterLink *outlink = ctx->outputs[0];
-    TInterlaceContext *tinterlace = ctx->priv;
-    AVFrame *cur, *next, *out;
-    int field, tff, ret;
-
-    av_frame_free(&tinterlace->cur);
-    tinterlace->cur  = tinterlace->next;
-    tinterlace->next = picref;
-
-    cur = tinterlace->cur;
-    next = tinterlace->next;
-    /* we need at least two frames */
-    if (!tinterlace->cur)
-        return 0;
-
-    switch (tinterlace->mode) {
-    case MODE_MERGEX2: /* move the odd frame into the upper field of the new image, even into
-                        * the lower field, generating a double-height video at same framerate */
-    case MODE_MERGE: /* move the odd frame into the upper field of the new image, even into
-             * the lower field, generating a double-height video at half framerate */
-        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    TInterlaceThreadData *rtd = &((TInterlaceThreadData *)tinterlace->thread_data)[plane];
+
+    if (!rtd)
+        return rtd;
+
+    rtd->out = out;
+    rtd->first = first;
+    rtd->second = second;
+    rtd->plane = plane;
+    rtd->tinterlace = tinterlace;
+    rtd->scale_h_plane12_factor = scale_h_plane12_factor;
+    rtd->scale_w_plane12_factor = scale_w_plane12_factor;
+
+    return rtd;
+}
+
+static void copy_all_planes(AVFilterContext *ctx,
+    TInterlaceContext *tinterlace,
+    const AVPixFmtDescriptor *desc,
+    AVFrame *out, AVFrame *first, AVFrame *second)
+{
+    int scale_w_plane12_factor = 1 << desc->log2_chroma_w;
+    int scale_h_plane12_factor = 1 << desc->log2_chroma_h;
+    int plane;
+
+    for (plane = 0; plane < desc->nb_components; plane++) {
+
+        TInterlaceThreadData *rtd = get_TInterlaceThreadData(out, first, second,
+            plane, tinterlace, scale_w_plane12_factor, scale_h_plane12_factor);
+
+        filter_frame_plane(ctx, rtd, plane, desc->nb_components);
+    }
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx            = inlink->dst;
+    TInterlaceContext *tinterlace   = ctx->priv;
+    AVFilterLink *outlink           = ctx->outputs[0];
+    const AVPixFmtDescriptor *desc  = av_pix_fmt_desc_get(outlink->format);
+    AVFrame *out, *first, *second;
+    int ret;
+
+    int tinterlace_mode = tinterlace->mode;
+
+    av_frame_free(&(tinterlace->prev_frame));
+    tinterlace->prev_frame = tinterlace->current_frame;
+    tinterlace->current_frame = in;
+    tinterlace->current_frame_index++;
+
+    // we process two frames at a time, thus only even frame indexes are considered
+    if (IS_ODD(tinterlace->current_frame_index)) {
+        if (tinterlace_mode == MODE_PAD || tinterlace_mode == MODE_MERGE_X2
+            || tinterlace_mode == MODE_INTERLACE_X2 || tinterlace_mode == MODE_MERGE_BFF
+            || tinterlace_mode == MODE_MERGE_TFF) {
+            // continue
+        } else {
+            return 0;
+        }
+    }
+
+    first   = tinterlace->prev_frame;
+    second  = tinterlace->current_frame;
+
+    switch (tinterlace_mode) {
+    case MODE_DROP_EVEN:
+    case MODE_DROP_ODD:
+        out = (tinterlace_mode == MODE_DROP_ODD) ? tinterlace->current_frame : tinterlace->prev_frame;
+        out = av_frame_clone(out);
+
         if (!out)
             return AVERROR(ENOMEM);
-        av_frame_copy_props(out, cur);
-        out->height = outlink->h;
-        out->interlaced_frame = 1;
-        out->top_field_first = 1;
-        out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1));
-
-        /* write odd frame lines into the upper field of the new frame */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)cur->data, cur->linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           FIELD_UPPER_AND_LOWER, 1, tinterlace->mode == MODE_MERGEX2 ? inlink->frame_count_out & 1 ? FIELD_LOWER : FIELD_UPPER : FIELD_UPPER, tinterlace->flags);
-        /* write even frame lines into the lower field of the new frame */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)next->data, next->linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           FIELD_UPPER_AND_LOWER, 1, tinterlace->mode == MODE_MERGEX2 ? inlink->frame_count_out & 1 ? FIELD_UPPER : FIELD_LOWER : FIELD_LOWER, tinterlace->flags);
-        if (tinterlace->mode != MODE_MERGEX2)
-            av_frame_free(&tinterlace->next);
+
+        out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base);
+        ret = ff_filter_frame(outlink, out);
         break;
 
-    case MODE_DROP_ODD:  /* only output even frames, odd  frames are dropped; height unchanged, half framerate */
-    case MODE_DROP_EVEN: /* only output odd  frames, even frames are dropped; height unchanged, half framerate */
-        out = av_frame_clone(tinterlace->mode == MODE_DROP_EVEN ? cur : next);
+    case MODE_MERGE:
+    case MODE_MERGE_X2:
+    case MODE_MERGE_TFF:
+    case MODE_MERGE_BFF:
+        if (tinterlace_mode == MODE_MERGE_X2 && tinterlace->current_frame_index == 1)
+            return 0;
+
+        if (tinterlace_mode == MODE_MERGE_BFF || tinterlace_mode == MODE_MERGE_TFF) {
+           if (!first)
+                return 0;
+
+            if (tinterlace->skip_next_frame) {
+                tinterlace->skip_next_frame = 0;
+                return 0;
+            }
+
+            if (first->interlaced_frame == 1 && second->interlaced_frame == 1)
+            {
+                if (first->top_field_first == second->top_field_first)
+                    return 0;
+                else if (tinterlace->mode == MODE_MERGE_BFF && first->top_field_first != 0)
+                    return 0;
+                else if (tinterlace->mode == MODE_MERGE_TFF && first->top_field_first != 1)
+                    return 0;
+            }
+        }
+
+        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+
         if (!out)
             return AVERROR(ENOMEM);
-        av_frame_free(&tinterlace->next);
+
+        av_frame_copy_props(out, first);
+        out->sample_aspect_ratio    = av_mul_q(first->sample_aspect_ratio, av_make_q(2, 1));
+        out->interlaced_frame       = 1;
+        out->top_field_first        = tinterlace_mode == MODE_MERGE_BFF ? 0 : 1;
+        out->height                 = outlink->h;
+
+        copy_all_planes(ctx, tinterlace, desc, out, first, second);
+
+        if (tinterlace_mode == MODE_MERGE_BFF || tinterlace_mode == MODE_MERGE_TFF)
+            tinterlace->skip_next_frame = 1;
+
+        out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base);
+        ret = ff_filter_frame(outlink, out);
         break;
 
-    case MODE_PAD: /* expand each frame to double height, but pad alternate
-                    * lines with black; framerate unchanged */
+    case MODE_PAD:
         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+
         if (!out)
             return AVERROR(ENOMEM);
-        av_frame_copy_props(out, cur);
+
+        av_frame_copy_props(out, second);
+        out->sample_aspect_ratio = av_mul_q(second->sample_aspect_ratio, av_make_q(2, 1));
         out->height = outlink->h;
-        out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1));
-
-        field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER;
-        /* copy upper and lower fields */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)cur->data, cur->linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           FIELD_UPPER_AND_LOWER, 1, field, tinterlace->flags);
-        /* pad with black the other field */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)tinterlace->black_data, tinterlace->black_linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           FIELD_UPPER_AND_LOWER, 1, !field, tinterlace->flags);
+
+        copy_all_planes(ctx, tinterlace, desc, out, first, second);
+
+        out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base);
+        ret = ff_filter_frame(outlink, out);
         break;
 
-        /* interleave upper/lower lines from odd frames with lower/upper lines from even frames,
-         * halving the frame rate and preserving image height */
-    case MODE_INTERLEAVE_TOP:    /* top    field first */
-    case MODE_INTERLEAVE_BOTTOM: /* bottom field first */
-        tff = tinterlace->mode == MODE_INTERLEAVE_TOP;
-        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    case MODE_INTERLEAVE_BOTTOM:
+    case MODE_INTERLEAVE_TOP:
+        out  = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+
         if (!out)
             return AVERROR(ENOMEM);
-        av_frame_copy_props(out, cur);
+
+        av_frame_copy_props(out, first);
+
+        copy_all_planes(ctx, tinterlace, desc, out, first, second);
+
         out->interlaced_frame = 1;
-        out->top_field_first = tff;
-
-        /* copy upper/lower field from cur */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)cur->data, cur->linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER,
-                           tinterlace->flags);
-        /* copy lower/upper field from next */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)next->data, next->linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER,
-                           tinterlace->flags);
-        av_frame_free(&tinterlace->next);
+        out->top_field_first = (tinterlace_mode == MODE_INTERLEAVE_TOP) ? 1 : 0;
+
+        out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base);
+        ret = ff_filter_frame(outlink, out);
         break;
-    case MODE_INTERLACEX2: /* re-interlace preserving image height, double frame rate */
-        /* output current frame first */
-        out = av_frame_clone(cur);
+
+    case MODE_INTERLACE_X2:
+        if (tinterlace->current_frame_index == 1)
+            return 0;
+
+        out = av_frame_clone(first);
+
         if (!out)
             return AVERROR(ENOMEM);
-        out->interlaced_frame = 1;
-        if (cur->pts != AV_NOPTS_VALUE)
-            out->pts = cur->pts*2;
 
+        // output first frame
+        out->pts = (AV_NOPTS_VALUE != first->pts ) ? first->pts * 2 : AV_NOPTS_VALUE;
+        out->interlaced_frame = 1;
         out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base);
-        if ((ret = ff_filter_frame(outlink, out)) < 0)
+        ret = ff_filter_frame(outlink, out);
+
+        if (ret < 0)
             return ret;
 
-        /* output mix of current and next frame */
-        tff = next->top_field_first;
+        // output the second frame interlaced with first frame
         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+
         if (!out)
             return AVERROR(ENOMEM);
-        av_frame_copy_props(out, next);
+
+        av_frame_copy_props(out, second);
         out->interlaced_frame = 1;
-        out->top_field_first = !tff;
-
-        if (next->pts != AV_NOPTS_VALUE && cur->pts != AV_NOPTS_VALUE)
-            out->pts = cur->pts + next->pts;
-        else
-            out->pts = AV_NOPTS_VALUE;
-        /* write current frame second field lines into the second field of the new frame */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)cur->data, cur->linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER,
-                           tinterlace->flags);
-        /* write next frame first field lines into the first field of the new frame */
-        copy_picture_field(tinterlace, out->data, out->linesize,
-                           (const uint8_t **)next->data, next->linesize,
-                           inlink->format, inlink->w, inlink->h,
-                           tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER,
-                           tinterlace->flags);
+        out->top_field_first = !out->top_field_first;
+        out->pts = first->pts + second->pts;
+        out->pts = ( first->pts == AV_NOPTS_VALUE || second->pts == AV_NOPTS_VALUE) ? AV_NOPTS_VALUE : out->pts;
+
+        copy_all_planes(ctx, tinterlace, desc, out, first, second);
+
+        out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base);
+        ret = ff_filter_frame(outlink, out);
         break;
+
     default:
         av_assert0(0);
     }
 
-    out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base);
-    ret = ff_filter_frame(outlink, out);
-    tinterlace->frame++;
-
     return ret;
 }
 
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    TInterlaceContext *tinterlace = ctx->priv;
+
+    av_frame_free(&tinterlace->prev_frame);
+    av_frame_free(&tinterlace->current_frame);
+
+    if (tinterlace->black_vec[0] != NULL)
+    {
+        av_freep(&tinterlace->black_vec[0]);
+    }
+
+    av_free(tinterlace->thread_data);
+}
+
 static const AVFilterPad tinterlace_inputs[] = {
     {
         .name         = "default",
@@ -522,29 +768,31 @@  static const AVFilterPad tinterlace_inputs[] = {
 
 static const AVFilterPad tinterlace_outputs[] = {
     {
-        .name         = "default",
-        .type         = AVMEDIA_TYPE_VIDEO,
-        .config_props = config_out_props,
+        .name           = "default",
+        .type           = AVMEDIA_TYPE_VIDEO,
+        .config_props   = config_out_props,
     },
     { NULL }
 };
 
 AVFilter ff_vf_tinterlace = {
     .name          = "tinterlace",
-    .description   = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
+    .description   = NULL_IF_CONFIG_SMALL("Various interlace frame manipulations"),
     .priv_size     = sizeof(TInterlaceContext),
+    .init          = init,
     .uninit        = uninit,
     .query_formats = query_formats,
     .inputs        = tinterlace_inputs,
     .outputs       = tinterlace_outputs,
     .priv_class    = &tinterlace_class,
+    .flags         = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
 };
 
-
 AVFilter ff_vf_interlace = {
     .name          = "interlace",
     .description   = NULL_IF_CONFIG_SMALL("Convert progressive video into interlaced."),
     .priv_size     = sizeof(TInterlaceContext),
+    .init          = init,
     .uninit        = uninit,
     .query_formats = query_formats,
     .inputs        = tinterlace_inputs,
diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile
index e31884aec0..b484c8bd1c 100644
--- a/libavfilter/x86/Makefile
+++ b/libavfilter/x86/Makefile
@@ -10,7 +10,6 @@  OBJS-$(CONFIG_HFLIP_FILTER)                  += x86/vf_hflip_init.o
 OBJS-$(CONFIG_HQDN3D_FILTER)                 += x86/vf_hqdn3d_init.o
 OBJS-$(CONFIG_IDET_FILTER)                   += x86/vf_idet_init.o
 OBJS-$(CONFIG_INTERLACE_FILTER)              += x86/vf_tinterlace_init.o
-OBJS-$(CONFIG_INTERLACE_FILTER)              += x86/vf_reinterlace_init.o
 OBJS-$(CONFIG_LIMITER_FILTER)                += x86/vf_limiter_init.o
 OBJS-$(CONFIG_MASKEDMERGE_FILTER)            += x86/vf_maskedmerge_init.o
 OBJS-$(CONFIG_NOISE_FILTER)                  += x86/vf_noise.o
diff --git a/libavfilter/x86/vf_reinterlace_init.c b/libavfilter/x86/vf_reinterlace_init.c
deleted file mode 100644
index 144db23beb..0000000000
--- a/libavfilter/x86/vf_reinterlace_init.c
+++ /dev/null
@@ -1,102 +0,0 @@ 
-/*
- * Copyright (c) 2018 Vasile Toncu <vasile.toncu@tremend.com>
- * Copyright (C) 2014 Kieran Kunhya <kierank@obe.tv>
- *
- * This file is part of FFmpeg.
- *
- * FFmpeg is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU 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.
- */
-
-#include "libavutil/attributes.h"
-#include "libavutil/cpu.h"
-#include "libavutil/internal.h"
-#include "libavutil/mem.h"
-#include "libavutil/x86/asm.h"
-#include "libavutil/x86/cpu.h"
-
-#include "libavfilter/reinterlace.h"
-
-#if CONFIG_GPL
-
-void ff_lowpass_line_sse2(uint8_t *dstp, ptrdiff_t linesize,
-                          const uint8_t *srcp, ptrdiff_t mref,
-                          ptrdiff_t pref, int clip_max);
-void ff_lowpass_line_avx (uint8_t *dstp, ptrdiff_t linesize,
-                          const uint8_t *srcp, ptrdiff_t mref,
-                          ptrdiff_t pref, int clip_max);
-void ff_lowpass_line_avx2 (uint8_t *dstp, ptrdiff_t linesize,
-                          const uint8_t *srcp, ptrdiff_t mref,
-                          ptrdiff_t pref, int clip_max);
-
-void ff_lowpass_line_16_sse2(uint8_t *dstp, ptrdiff_t linesize,
-                             const uint8_t *srcp, ptrdiff_t mref,
-                             ptrdiff_t pref, int clip_max);
-void ff_lowpass_line_16_avx (uint8_t *dstp, ptrdiff_t linesize,
-                             const uint8_t *srcp, ptrdiff_t mref,
-                             ptrdiff_t pref, int clip_max);
-void ff_lowpass_line_16_avx2 (uint8_t *dstp, ptrdiff_t linesize,
-                             const uint8_t *srcp, ptrdiff_t mref,
-                             ptrdiff_t pref, int clip_max);
-
-void ff_lowpass_line_complex_sse2(uint8_t *dstp, ptrdiff_t linesize,
-                                  const uint8_t *srcp, ptrdiff_t mref,
-                                  ptrdiff_t pref, int clip_max);
-
-void ff_lowpass_line_complex_12_sse2(uint8_t *dstp, ptrdiff_t linesize,
-                                     const uint8_t *srcp, ptrdiff_t mref,
-                                     ptrdiff_t pref, int clip_max);
-
-av_cold void ff_reinterlace_init_x86(ReInterlaceContext *reinterlace)
-{
-    int cpu_flags = av_get_cpu_flags();
-
-    if (reinterlace->bit_depth > 8) {
-        if (EXTERNAL_SSE2(cpu_flags)) {
-            if (!(reinterlace->flags & FLAG_CVLPF))
-                reinterlace->lowpass_line = ff_lowpass_line_16_sse2;
-            else
-                reinterlace->lowpass_line = ff_lowpass_line_complex_12_sse2;
-        }
-        if (EXTERNAL_AVX(cpu_flags))
-            if (!(reinterlace->flags & FLAG_CVLPF))
-                reinterlace->lowpass_line = ff_lowpass_line_16_avx;
-        if (EXTERNAL_AVX2_FAST(cpu_flags)) {
-            if (!(reinterlace->flags & FLAG_CVLPF)) {
-                reinterlace->lowpass_line = ff_lowpass_line_16_avx2;
-            }
-        }
-    } else {
-        if (EXTERNAL_SSE2(cpu_flags)) {
-            if (!(reinterlace->flags & FLAG_CVLPF))
-                reinterlace->lowpass_line = ff_lowpass_line_sse2;
-            else
-                reinterlace->lowpass_line = ff_lowpass_line_complex_sse2;
-        }
-        if (EXTERNAL_AVX(cpu_flags))
-            if (!(reinterlace->flags & FLAG_CVLPF))
-                reinterlace->lowpass_line = ff_lowpass_line_avx;
-        if (EXTERNAL_AVX2_FAST(cpu_flags)) {
-            if (!(reinterlace->flags & FLAG_CVLPF)) {
-                reinterlace->lowpass_line = ff_lowpass_line_avx2;
-            }
-        }
-    }
-}
-
-#elif /* CONFIG_GPL */
-
-av_cold void ff_reinterlace_init_x86(ReInterlaceContext *s) {}
-
-#endif /* CONFIG_GPL */
diff --git a/libavfilter/x86/vf_tinterlace_init.c b/libavfilter/x86/vf_tinterlace_init.c
index 2c9b1de581..dcb4834a77 100644
--- a/libavfilter/x86/vf_tinterlace_init.c
+++ b/libavfilter/x86/vf_tinterlace_init.c
@@ -1,4 +1,5 @@ 
 /*
+ * Copyright (c) 2018 Vasile Toncu <vasile.toncu@tremend.com>
  * Copyright (C) 2014 Kieran Kunhya <kierank@obe.tv>
  *
  * This file is part of FFmpeg.
@@ -27,6 +28,8 @@ 
 
 #include "libavfilter/tinterlace.h"
 
+#if CONFIG_GPL
+
 void ff_lowpass_line_sse2(uint8_t *dstp, ptrdiff_t linesize,
                           const uint8_t *srcp, ptrdiff_t mref,
                           ptrdiff_t pref, int clip_max);
@@ -55,39 +58,45 @@  void ff_lowpass_line_complex_12_sse2(uint8_t *dstp, ptrdiff_t linesize,
                                      const uint8_t *srcp, ptrdiff_t mref,
                                      ptrdiff_t pref, int clip_max);
 
-av_cold void ff_tinterlace_init_x86(TInterlaceContext *s)
+av_cold void ff_tinterlace_init_x86(TInterlaceContext *tinterlace)
 {
     int cpu_flags = av_get_cpu_flags();
 
-    if (s->csp->comp[0].depth > 8) {
+    if (tinterlace->bit_depth > 8) {
         if (EXTERNAL_SSE2(cpu_flags)) {
-            if (!(s->flags & TINTERLACE_FLAG_CVLPF))
-                s->lowpass_line = ff_lowpass_line_16_sse2;
+            if (!(tinterlace->flags & FLAG_CVLPF))
+                tinterlace->lowpass_line = ff_lowpass_line_16_sse2;
             else
-                s->lowpass_line = ff_lowpass_line_complex_12_sse2;
+                tinterlace->lowpass_line = ff_lowpass_line_complex_12_sse2;
         }
         if (EXTERNAL_AVX(cpu_flags))
-            if (!(s->flags & TINTERLACE_FLAG_CVLPF))
-                s->lowpass_line = ff_lowpass_line_16_avx;
+            if (!(tinterlace->flags & FLAG_CVLPF))
+                tinterlace->lowpass_line = ff_lowpass_line_16_avx;
         if (EXTERNAL_AVX2_FAST(cpu_flags)) {
-            if (!(s->flags & TINTERLACE_FLAG_CVLPF)) {
-                s->lowpass_line = ff_lowpass_line_16_avx2;
+            if (!(tinterlace->flags & FLAG_CVLPF)) {
+                tinterlace->lowpass_line = ff_lowpass_line_16_avx2;
             }
         }
     } else {
         if (EXTERNAL_SSE2(cpu_flags)) {
-            if (!(s->flags & TINTERLACE_FLAG_CVLPF))
-                s->lowpass_line = ff_lowpass_line_sse2;
+            if (!(tinterlace->flags & FLAG_CVLPF))
+                tinterlace->lowpass_line = ff_lowpass_line_sse2;
             else
-                s->lowpass_line = ff_lowpass_line_complex_sse2;
+                tinterlace->lowpass_line = ff_lowpass_line_complex_sse2;
         }
         if (EXTERNAL_AVX(cpu_flags))
-            if (!(s->flags & TINTERLACE_FLAG_CVLPF))
-                s->lowpass_line = ff_lowpass_line_avx;
+            if (!(tinterlace->flags & FLAG_CVLPF))
+                tinterlace->lowpass_line = ff_lowpass_line_avx;
         if (EXTERNAL_AVX2_FAST(cpu_flags)) {
-            if (!(s->flags & TINTERLACE_FLAG_CVLPF)) {
-                s->lowpass_line = ff_lowpass_line_avx2;
+            if (!(tinterlace->flags & FLAG_CVLPF)) {
+                tinterlace->lowpass_line = ff_lowpass_line_avx2;
             }
         }
     }
 }
+
+#elif /* CONFIG_GPL */
+
+av_cold void ff_tinterlace_init_x86(ReInterlaceContext *s) {}
+
+#endif /* CONFIG_GPL */
-- 
2.17.1