Fixed tabs. On Mon, Jul 16, 2018 at 4:50 PM, Vasile Toncu <vasile.to...@tremend.com> wrote:
> Hello, > > I have updated patch 2, it shall work now. > > Thank you, > Vasile Toncu > > > On Mon, Jul 16, 2018 at 4:49 PM, Vasile Toncu <vasile.to...@tremend.com> > wrote: > >> Hello, >> >> I have updated patch 2, it shall work now. >> >> Thank you, >> Vasile Toncu >> >> On Wed, May 30, 2018 at 2:00 PM, Vasile Toncu <vasile.to...@tremend.com> >> wrote: >> >>> Hello, >>> >>> Sorry for the late reply, I've been working on other projects. >>> Here is patch 2, which adds vf_reinterlace filter. To my understanding, >>> patch 2 and patch 3 will be merged at the same time, so there is no need to >>> add documentation for reinterlace as it will replace tinterlace. >>> >>> Thank you, >>> Vasile Toncu >>> >> >> >
From 2e213ec9579e9fd38a9085c437208f1fd54fb423 Mon Sep 17 00:00:00 2001 From: Vasile Toncu <vasile.to...@tremend.com> Date: Mon, 16 Jul 2018 15:24:51 +0200 Subject: [PATCH] Patch 2 - Add reinterlace --- libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/reinterlace.h | 105 ++++ libavfilter/vf_reinterlace.c | 775 ++++++++++++++++++++++++++ libavfilter/x86/Makefile | 1 + libavfilter/x86/vf_reinterlace_init.c | 102 ++++ 6 files changed, 985 insertions(+) create mode 100644 libavfilter/reinterlace.h create mode 100644 libavfilter/vf_reinterlace.c create mode 100644 libavfilter/x86/vf_reinterlace_init.c diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 5d4549e24c..49fad08290 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -245,6 +245,7 @@ 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 521bc53164..874d25e72f 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -347,6 +347,7 @@ 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 new file mode 100644 index 0000000000..cb512c7639 --- /dev/null +++ b/libavfilter/reinterlace.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017 Vasile Toncu <vasile.to...@tremend.com> + * Copyright (c) 2017 Thomas Mundt <tmund...@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/vf_reinterlace.c b/libavfilter/vf_reinterlace.c new file mode 100644 index 0000000000..d3f8f374ae --- /dev/null +++ b/libavfilter/vf_reinterlace.c @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2018 Vasile Toncu <vasile.to...@tremend.com> + * Copyright (c) 2017 Thomas Mundt <tmund...@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/x86/Makefile b/libavfilter/x86/Makefile index b484c8bd1c..e31884aec0 100644 --- a/libavfilter/x86/Makefile +++ b/libavfilter/x86/Makefile @@ -10,6 +10,7 @@ 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 new file mode 100644 index 0000000000..144db23beb --- /dev/null +++ b/libavfilter/x86/vf_reinterlace_init.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018 Vasile Toncu <vasile.to...@tremend.com> + * Copyright (C) 2014 Kieran Kunhya <kier...@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 */ -- 2.17.1
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel