[FFmpeg-devel] [PATCH] added reitnerlace filter
Nicolas George
george at nsup.org
Wed Dec 27 15:02:00 EET 2017
Thanks for the patch. Initial comments below.
Vasile Toncu (2017-12-27):
> ---
> 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).
Your editor seems to have broken UTF-8 text. Same at a few places below.
> @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
This looks spurious.
> 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);
> +
Move it to the alphabetical place within video filters.
> /* 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
This header is included only once. If there is no good reason to make it
a separate header, put the declarations in the source files.
> @@ -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"
We usually try to keep includes in alphabetical order.
> +#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;
If this is supposed to count the number of frames in a whole stream,
then make it (u)int64_t.
> +
> + uint8_t *black_vec[4];
> +
> + int skip_next_frame;
> +
> + void *thread_data;
Use the correct type.
> +
> +} ReInterlaceContext;
> +
> +#define OFFSET(x) offsetof(ReInterlaceContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption reinterlace_options[] = {
Declaring a static object in a header seems like a bad idea.
> + { "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"},
The documentation field is not very user friendly. Same below.
> +
> + { "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, \
Used only once, probably better inlined.
> + 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;
Not required.
> +
> + for (int i = 0; i < 4; i++)
Our coding style still forbids declaring the loop variable here.
> + reinterlace->black_vec[i] = NULL;
> +
> + reinterlace->skip_next_frame = 0;
Not required either.
> +
> + reinterlace->thread_data = (ReInterlaceThreadData *) malloc(4 * sizeof(ReInterlaceThreadData));
Get rid of that cast, and use a function in the av_malloc() family. Same
below.
> +
> + 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;
Why?
> + 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);
It could be reached if the user sets an invalid integer value for the
mode => no assert, proper error message and code.
> +
> + }
> +
> + 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++)
Use the same type as the bounds.
> + //for (int i = width; i--; )
Cleanup.
> + 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;
Why?
> +
> + 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] )
Unusual spacing. Same at a few ohther places.
> + 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);
Missing error check.
> + 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,
> +};
Regards,
--
Nicolas George
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20171227/16612105/attachment.sig>
More information about the ffmpeg-devel
mailing list