[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