[FFmpeg-devel] [PATCH] reinterlace filter

Vasile Toncu vasile.toncu at tremend.com
Thu Dec 28 14:52:34 EET 2017


Hello,

Reinterlace filter is a new video filter that does various interlace/interleave/merge ops between lines of consecutive frames.

It adds two new modes to the already existing tinterlace filter, merge_bff and merge_tff.

It is under LGPL license and starts from ffmpeg version n3.0.2.

This is my second submision attempt, with some modifications from last attempt.

---
 doc/filters.texi             |  75 +++++
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/vf_reinterlace.c | 711 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 788 insertions(+)
 create mode 100644 libavfilter/vf_reinterlace.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 68f54f1..b3cf298 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -13191,6 +13191,81 @@ pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1.
 @table @option
 @end table
 
+ at section reinterlace
+Reinterlace filter does various interlace operations with the frames of a video.
+
+ at table @option
+
+ at item mode
+The mode of the filter
+
+The permitted values for @var{mode} are:
+
+ at table @samp
+ at item merge, 0
+Merges lines of two consecutive frames. Skips even frames. The output has half frame rate of the input.
+
+ at item drop_even, 1
+Drops even frames. The output has half frame rate of the input.
+
+ at item drop_odd, 2
+Drop odd frames. The output has half frame rate of the input.
+
+ at item pad, 3
+Merges all the frames with a black frame. The output has the same frame rate as as the input.
+
+
+ at item interleave_top, 4
+Interleaves lines of two consecutive frames. Skips even frames. The output has half frame rate of the input.
+
+ at item interleave_bottom, 5
+Interleaves lines of two consecutive frames. Skips even frames. The output has half frame rate of the input.
+
+ at item interlacex2, 6
+For every frames in the input frame adds another one which is obtaining by the interlace of two consecutive frames. 
+The output has double frame rate of the input.
+
+ at item mergex2, 7
+Merge every frame with the next frame. The output has the same frame rate as as the input.
+
+ at item merge_tff, 8
+Merges the frames of the input considering also the parity and the top_filed_first information of the frames.
+
+The rules for the @var{merge_tff} are the folowng: 
+
+    1. ensure the odd frame metadata indicates a top field, @*
+    2. ensure the even frame metadata indicates a bottom field, @*
+    3. move the odd frame into the upper field of the new image, @*
+    4. move the even frame into the lower field of the new image, @*
+    5. if frames are out of order (bottom field then top field), drop the first field @*
+    6. if frames are duplicates (top field then top field), drop the first field @*
+    7. if frames don't have interlace metadata, merge as if they were in the right order @*
+
+
+ at item merge_bff, 9
+Merges the frames of the input considering also the parity and the top_filed_first information of the frames.
+
+The rules for the @var{merge_bff} are similar with those for @var{merge_tff}, albeit inverted appropriately.
+
+ at end table
+
+Default mode is @code{merge, 0}.
+
+ at item flags
+One can add various flags to the reitnerlace filter.
+
+The permitted values for @var{flags} are:
+
+ at table @option
+ at item low_pass_filter, 1
+Before copying a line of a frame, it gots filtered using a simple low pass filter with the upper and lowwer frame lines.
+
+Vertical low-pass filtering can only be enabled for @option{mode}
+ at var{interleave_top} and @var{interleave_bottom}.
+
+ at end table
+ at end table
+
 @c man end VIDEO FILTERS
 
 @chapter Video Sources
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 8916588..b2e9a50 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -213,6 +213,7 @@ OBJS-$(CONFIG_PULLUP_FILTER)                 += vf_pullup.o
 OBJS-$(CONFIG_QP_FILTER)                     += vf_qp.o
 OBJS-$(CONFIG_RANDOM_FILTER)                 += vf_random.o
 OBJS-$(CONFIG_REALTIME_FILTER)               += f_realtime.o
+OBJS-$(CONFIG_REINTERLACE_FILTER)            += vf_reinterlace.o
 OBJS-$(CONFIG_REMOVEGRAIN_FILTER)            += vf_removegrain.o
 OBJS-$(CONFIG_REMOVELOGO_FILTER)             += bbox.o lswsutils.o lavfutils.o vf_removelogo.o
 OBJS-$(CONFIG_REPEATFIELDS_FILTER)           += vf_repeatfields.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index fa7d304..5333ac1 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -233,6 +233,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(QP,             qp,             vf);
     REGISTER_FILTER(RANDOM,         random,         vf);
     REGISTER_FILTER(REALTIME,       realtime,       vf);
+    REGISTER_FILTER(REINTERLACE,    reinterlace,    vf);
     REGISTER_FILTER(REMOVEGRAIN,    removegrain,    vf);
     REGISTER_FILTER(REMOVELOGO,     removelogo,     vf);
     REGISTER_FILTER(REPEATFIELDS,   repeatfields,   vf);
diff --git a/libavfilter/vf_reinterlace.c b/libavfilter/vf_reinterlace.c
new file mode 100644
index 0000000..5383b7b
--- /dev/null
+++ b/libavfilter/vf_reinterlace.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (c) 2017 Vasile Toncu <u pkh me>
+ *
+ * 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 
+ *
+ * @author Vasile Toncu  ( toncu.vasile gmail com )
+ *
+ * @see https://en.wikipedia.org/wiki/Interlaced_video
+ */
+
+
+#include <stdint.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"
+
+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_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 skip_next_frame;
+
+    void *thread_data;
+
+} ReInterlaceContext;
+
+#define OFFSET(x) offsetof(ReInterlaceContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+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"},
+    { "merge_tff",         "merge frames using top_field_first information",            0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE_TFF},            INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "merge_bff",         "Mmerge frames using top_field_first information",            0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE_BFF},            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"},
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(reinterlace);
+
+#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;
+
+#define PIXEL_FORMATS AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,   \
+        AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P,                 \
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,                 \
+        AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ422P,                \
+        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ420P,               \
+        AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ440P,               \
+        AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV422P9,               \
+        AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV444P10,              \
+        AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10,             \
+        AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV444P12,             \
+        AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,             \
+        AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV444P14,             \
+        AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,             \
+        AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16,             \
+        AV_PIX_FMT_YUV420P16, AV_PIX_FMT_GRAY8,                 \
+        AV_PIX_FMT_NONE
+
+static enum AVPixelFormat all_pix_fmts[] = {
+        AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,                 \
+        AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P,                 \
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,                 \
+        AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ422P,                \
+        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ420P,               \
+        AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ440P,               \
+        AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV422P9,               \
+        AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV444P10,              \
+        AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10,             \
+        AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV444P12,             \
+        AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,             \
+        AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV444P14,             \
+        AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,             \
+        AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16,             \
+        AV_PIX_FMT_YUV420P16, AV_PIX_FMT_GRAY8,                 \
+        AV_PIX_FMT_NONE
+};
+
+
+
+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));
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    //const ReInterlaceContext *reinterlace = ctx->priv;
+
+    AVFilterFormats *fmts_list;
+
+    fmts_list = ff_make_format_list(all_pix_fmts);
+
+    if (!fmts_list)
+        return AVERROR(ENOMEM);
+
+    return ff_set_common_formats(ctx, fmts_list);
+}
+
+static int config_out_props(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AVFilterLink *inlink = outlink->src->inputs[0];
+    ReInterlaceContext *reinterlace = ctx->priv;
+    int r_mode = reinterlace->mode;
+
+    switch (r_mode) {
+    case MODE_MERGE:
+        outlink->h = 2 * inlink->h;
+        outlink->time_base  = av_mul_q(inlink->time_base , (AVRational){2,1});
+        outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2});
+        outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, av_make_q(2, 1));  
+        break;
+
+    case MODE_PAD:
+        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_DROP_EVEN:
+    case MODE_DROP_ODD:
+    case MODE_INTERLEAVE_TOP:
+    case MODE_INTERLEAVE_BOTTOM:
+        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->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->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_BFF:
+    case MODE_MERGE_TFF:
+        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});
+        break;
+
+    default:
+        av_log(ctx, AV_LOG_VERBOSE, "invalid value for mode");
+        av_assert0(0);
+
+    }
+
+    if (reinterlace->flags & FLAG_VLPF)
+        if (r_mode != MODE_INTERLEAVE_TOP && r_mode != MODE_INTERLEAVE_BOTTOM)
+            reinterlace->flags &= ~FLAG_VLPF;
+    
+    return 0;
+}
+
+static void copy_line_lowpass(uint8_t *to, ptrdiff_t width, uint8_t *from,
+                           uint8_t *from_up, uint8_t *from_down)
+{
+    ptrdiff_t i;
+
+    for (i = 0; i < width; i++)
+        to[i] = (1 + from[i] + from[i] + from_up[i] + from_down[i]) >> 2;
+}
+
+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 r_mode = reinterlace->mode;
+
+    int x = (1 == plane || 2 == plane) ? rtd->scale_w_plane12_factor : 1; 
+    int y = (1 == plane || 2 == plane) ? rtd->scale_h_plane12_factor : 1;
+    int i, ls_offset;
+    int offset1, offset2, offset3, offset4;
+
+    switch (r_mode) {
+    case MODE_MERGE:
+        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_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], 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], second->linesize[plane], second->width / x, second->height / y);
+        break;
+
+    case MODE_INTERLEAVE_BOTTOM:
+    case MODE_INTERLEAVE_TOP:
+        y = y * 2;
+  
+        if (reinterlace->flags & FLAG_VLPF) {
+
+            int lines, cols;
+            AVFrame *from_frame;
+            uint8_t *from, *to;
+            int from_step, to_step;
+
+            lines = (MODE_INTERLEAVE_TOP == r_mode) ? (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 (MODE_INTERLEAVE_BOTTOM == r_mode) {
+                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
+            copy_line_lowpass(to, cols, from, from, from + from_frame->linesize[plane]);
+            to += to_step;
+            from += from_step;
+
+            for (i = lines - 2; i; i--) {
+                //from_up = (lines == i) ? from : from - from_frame->linesize[plane];
+                //from_down = (1 == i) ? from : from + from_frame->linesize[plane];
+                copy_line_lowpass(to, cols, from, from - from_frame->linesize[plane], from + from_frame->linesize[plane]);
+                to += to_step;
+                from += from_step;
+            }
+
+            // when i == 1
+            copy_line_lowpass(to, cols, from, from - from_frame->linesize[plane], from);
+            to += to_step;
+            from += from_step;
+
+            lines = (MODE_INTERLEAVE_BOTTOM == r_mode) ? ((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 (MODE_INTERLEAVE_TOP == r_mode) {
+                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
+            copy_line_lowpass(to, cols, from, from, from + from_frame->linesize[plane]);
+            to += to_step;
+            from += from_step;
+
+            for (i = lines - 2; i; i--) {
+                //from_up = (lines == i) ? from : from - from_frame->linesize[plane];
+                //from_down = (1 == i) ? from : from + from_frame->linesize[plane];
+                copy_line_lowpass(to, cols, from, from - from_frame->linesize[plane], from + from_frame->linesize[plane]);
+                to += to_step;
+                from += from_step;
+            }
+
+            // when i == 1
+            copy_line_lowpass(to, cols, from, from - from_frame->linesize[plane], from);
+            to += to_step;
+            from += from_step;
+
+        } else {
+            offset1 = (MODE_INTERLEAVE_TOP == r_mode) ? 0 : out->linesize[plane];
+            offset2 = (MODE_INTERLEAVE_TOP == r_mode) ? 0 : first->linesize[plane];
+            offset3 = (MODE_INTERLEAVE_TOP == r_mode) ? out->linesize[plane]    : 0;
+            offset4 = (MODE_INTERLEAVE_TOP == r_mode) ? 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 = (MODE_MERGE_TFF == r_mode) ? 0 : out->linesize[plane];
+        offset2 = (MODE_MERGE_TFF == r_mode) ? 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;
+}
+
+/** 
+ * alocate memory for a black frame 
+ */
+static int init_black_buffers(ReInterlaceContext *reinterlace, AVFrame *frame, int format)
+{
+    int black_vec_size = frame->width * frame->height * 3;
+    int val_black = 16;
+    int i;
+
+    if (AV_PIX_FMT_YUVJ420P == format ||
+        AV_PIX_FMT_YUVJ422P == format || 
+        AV_PIX_FMT_YUVJ440P == format || 
+        AV_PIX_FMT_YUVJ444P == format) {
+
+        val_black = 0;
+    
+    }
+
+    for (i = 0; i < 4; i++) {
+        reinterlace->black_vec[i] = av_malloc(black_vec_size);
+
+        if (!reinterlace->black_vec[i] )
+            return AVERROR(ENOMEM);
+
+        memset(reinterlace->black_vec[i], (0 == i || 3 == i ? val_black : 128),  black_vec_size);
+    }
+
+    return 0;
+}
+
+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));
+        //filter_frame_plane(ctx, rtd, plane, desc->nb_components);
+    }
+}
+
+        
+
+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 r_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 (MODE_PAD == r_mode || MODE_MERGE_X2 == r_mode 
+            || MODE_INTERLACE_X2 == r_mode || MODE_MERGE_BFF == r_mode 
+            || MODE_MERGE_TFF == r_mode) {
+        } else {
+            return 0;
+        }
+    }
+
+    if (1 == reinterlace->current_frame_index) {
+        ret = init_black_buffers(reinterlace, in, outlink->format);
+
+        if (ret < 0)
+           return ret; 
+    }
+
+    first   = reinterlace->prev_frame;
+    second  = reinterlace->current_frame;
+
+    switch (r_mode) {
+    case MODE_DROP_EVEN:
+    case MODE_DROP_ODD:
+        out = (r_mode == MODE_DROP_ODD) ? reinterlace->current_frame : reinterlace->prev_frame;
+        out = av_frame_clone(out);
+
+        if (!out)
+            return AVERROR(ENOMEM);
+
+        out->pts = out->pts >> 1;
+        ret = ff_filter_frame(outlink, out);
+        break;
+
+    case MODE_MERGE:
+    case MODE_MERGE_X2:
+    case MODE_MERGE_TFF:
+    case MODE_MERGE_BFF:
+        if (MODE_MERGE_X2 == r_mode && 1 == reinterlace->current_frame_index)
+            return 0;
+
+        if (MODE_MERGE_BFF == r_mode || MODE_MERGE_TFF == r_mode) {
+           if (!first)
+                return 0;
+
+            if (reinterlace->skip_next_frame) {
+                reinterlace->skip_next_frame = 0;
+                return 0;
+            }
+
+            if (1 == first->interlaced_frame && 1 == second->interlaced_frame)
+            {
+                if (first->top_field_first == second->top_field_first)
+                    return 0;
+                else if (MODE_MERGE_BFF == reinterlace->mode && first->top_field_first != 0)
+                    return 0;
+                else if (MODE_MERGE_TFF == reinterlace->mode && 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        = MODE_MERGE_BFF == r_mode ? 0 : 1;
+        out->height                 = outlink->h;
+
+        if (MODE_MERGE == r_mode) 
+            out->pts = out->pts >> 1;
+
+        copy_all_planes(ctx, reinterlace, desc, out, first, second);
+
+        if (MODE_MERGE_BFF == r_mode || MODE_MERGE_TFF == r_mode) 
+            reinterlace->skip_next_frame = 1;
+
+        ret = ff_filter_frame(outlink, out);
+        break;
+
+    case MODE_PAD:
+        out = ff_get_video_buffer(outlink, outlink->w, outlink->h * 2);
+
+        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);
+
+        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->pts = out->pts >> 1;
+        out->interlaced_frame = 1;
+        out->top_field_first = (MODE_INTERLEAVE_TOP == r_mode) ? 1 : 0;
+        ret = ff_filter_frame(outlink, out);
+        break;
+
+    case MODE_INTERLACE_X2:
+        if (1 == reinterlace->current_frame_index)
+            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;      
+        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 = (AV_NOPTS_VALUE == first->pts || AV_NOPTS_VALUE == second->pts) ? AV_NOPTS_VALUE : out->pts;
+
+        copy_all_planes(ctx, reinterlace, desc, out, first, second);
+
+        ret = ff_filter_frame(outlink, out);
+        break;
+
+    default:
+        av_assert0(0);
+    }
+
+    return ret;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    ReInterlaceContext *reinterlace = ctx->priv;
+    int i;
+
+    for (i = 0; i < 4; i++)
+        if (reinterlace->black_vec[i])
+            av_free(reinterlace->black_vec[i]);
+
+    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,
+};
-- 
2.7.4



More information about the ffmpeg-devel mailing list