[FFmpeg-devel] [PATCH] added reitnerlace filter

Vasile Toncu vasile.toncu at tremend.com
Wed Dec 27 14:39:08 EET 2017


---
 doc/filters.texi             |  87 ++++++-
 libavfilter/Makefile         |   2 +
 libavfilter/allfilters.c     |   2 +
 libavfilter/reinterlace.h    | 130 ++++++++++
 libavfilter/vf_reinterlace.c | 597 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 812 insertions(+), 6 deletions(-)
 create mode 100644 libavfilter/reinterlace.h
 create mode 100644 libavfilter/vf_reinterlace.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 68f54f1..370be9b 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -564,13 +564,13 @@ select RIAA.
 @item cd
 select Compact Disc (CD).
 @item 50fm
-select 50µs (FM).
+select 50µs (FM).
 @item 75fm
-select 75µs (FM).
+select 75µs (FM).
 @item 50kf
-select 50µs (FM-KF).
+select 50µs (FM-KF).
 @item 75kf
-select 75µs (FM-KF).
+select 75µs (FM-KF).
 @end table
 @end table
 
@@ -7346,7 +7346,7 @@ If not set, the filter will use the QP from the video stream (if available).
 @item strength
 Set filter strength. It accepts an integer in range -15 to 32. Lower values mean
 more details but also more artifacts, while higher values make the image smoother
-but also blurrier. Default value is @code{0} − PSNR optimal.
+but also blurrier. Default value is @code{0} − PSNR optimal.
 
 @item use_bframe_qp
 Enable the use of the QP from the B-Frames if set to @code{1}. Using this
@@ -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
@@ -15897,4 +15972,4 @@ movie=dvd.vob:s=v:0+#0x81 [video] [audio]
 @end example
 @end itemize
 
- at c man end MULTIMEDIA SOURCES
+ at c man end MULTIMEDIA SOURCES
\ No newline at end of file
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 8916588..606dfe0 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -286,6 +286,8 @@ OBJS-$(CONFIG_TESTSRC2_FILTER)               += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
+OBJS-$(CONFIG_REINTERLACE_FILTER)            += vf_reinterlace.o
+
 # multimedia filters
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
 OBJS-$(CONFIG_AHISTOGRAM_FILTER)             += avf_ahistogram.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index fa7d304..fa3c3d1 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -306,6 +306,8 @@ void avfilter_register_all(void)
 
     REGISTER_FILTER(NULLSINK,       nullsink,       vsink);
 
+    REGISTER_FILTER(REINTERLACE,    reinterlace,    vf);
+
     /* multimedia filters */
     REGISTER_FILTER(ADRAWGRAPH,     adrawgraph,     avf);
     REGISTER_FILTER(AHISTOGRAM,     ahistogram,     avf);
diff --git a/libavfilter/reinterlace.h b/libavfilter/reinterlace.h
new file mode 100644
index 0000000..924f347
--- /dev/null
+++ b/libavfilter/reinterlace.h
@@ -0,0 +1,130 @@
+/*
+ * 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 "libavutil/avassert.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/imgutils.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.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;
+    int 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",             "MODE_MERGE",                0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE},                INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "drop_even",         "MODE_DROP_EVEN",            0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN},            INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "drop_odd",          "MODE_DROP_ODD",             0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD},             INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "pad",               "MODE_PAD",                  0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD},                  INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "interleave_top",    "MODE_INTERLEAVE_TOP",       0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP},       INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "interleave_bottom", "MODE_INTERLEAVE_BOTTOM",    0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM},    INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "interlacex2",       "MODE_INTERLACE_X2",         0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACE_X2},         INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "mergex2",           "MODE_MERGE_X2",             0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE_X2},             INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "merge_tff",         "MODE_MERGE_TFF",            0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE_TFF},            INT_MIN, INT_MAX, FLAGS, "mode"},
+    { "merge_bff",         "MODE_MERGE_BFF",            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",   "FLAG_VLPF",                 0, AV_OPT_TYPE_CONST, {.i64 = FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags"},
+    { "vlpf",              "FLAG_VLPF",                 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
+
diff --git a/libavfilter/vf_reinterlace.c b/libavfilter/vf_reinterlace.c
new file mode 100644
index 0000000..ee28593
--- /dev/null
+++ b/libavfilter/vf_reinterlace.c
@@ -0,0 +1,597 @@
+/*
+ * 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 "reinterlace.h"
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    ReInterlaceContext *reinterlace = ctx->priv;
+
+    reinterlace->current_frame_index = 0;
+    reinterlace->prev_frame = NULL;
+    reinterlace->current_frame = NULL;
+
+    for (int i = 0; i < 4; i++)
+        reinterlace->black_vec[i] = NULL;
+
+    reinterlace->skip_next_frame = 0;
+
+    reinterlace->thread_data = (ReInterlaceThreadData *) malloc(4 * sizeof(ReInterlaceThreadData));
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    //const ReInterlaceContext *reinterlace = ctx->priv;
+
+    // TODO add more necesary formats
+    static const enum AVPixelFormat all_pix_fmts[] = {
+        PIXEL_FORMATS
+    }; 
+
+    AVFilterFormats *fmts_list;
+    const enum AVPixelFormat *pix_fmts = NULL;
+
+    pix_fmts = all_pix_fmts;
+    fmts_list = ff_make_format_list(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_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)
+{
+    for (int i = 0; i < width; i++)
+    //for (int 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 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 (int 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 (int 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 val0 = 16;
+    int val128 = 128;
+
+    if (AV_PIX_FMT_YUVJ420P == format ||
+        AV_PIX_FMT_YUVJ422P == format || 
+        AV_PIX_FMT_YUVJ440P == format || 
+        AV_PIX_FMT_YUVJ444P == format) {
+
+        val0 = 0;
+    
+    }
+
+    for (int i = 0; i < 4; i++) {
+        reinterlace->black_vec[i] = (uint8_t *) malloc(black_vec_size);
+
+        if ( !reinterlace->black_vec[i] )
+            return AVERROR(ENOMEM);
+
+        memset(reinterlace->black_vec[i], (0 == i || 3 == i ? val0 : val128),  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;
+
+    for (int 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);
+        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])
+            free (reinterlace->black_vec[i]);
+
+    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