[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