[FFmpeg-devel] [PATCH] lavfi: port MP il filter

Stefano Sabatini stefasab at gmail.com
Sat Feb 9 16:19:59 CET 2013


On date Friday 2013-02-08 18:19:53 +0000, Paul B Mahol encoded:
> Signed-off-by: Paul B Mahol <onemda at gmail.com>
> ---
>  doc/filters.texi         |  46 +++++++++++
>  libavfilter/Makefile     |   1 +
>  libavfilter/allfilters.c |   1 +
>  libavfilter/vf_il.c      | 199 +++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 247 insertions(+)
>  create mode 100644 libavfilter/vf_il.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 1f22f8d..920336d 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -4537,6 +4537,52 @@ Useful for enlarging pixel art images without reducing sharpness.
>  @section swapuv
>  Swap U & V plane.
>  
> + at section il
> +(De)interleaves lines.

Deinterleave or interleave lines.

> +
> +Process interlaced images pre-field without deinterlacing them.

This filter allows to process interlaced images fields without
deinterlacing them.

Deinterleaving splits the input frame into 2 fields (so called half
pictures). Odd lines are moved to the top half of the output image,
even lines to the bottom half. You can process (filter) them
independently and then re-interleave them.

> +
> +It accepts a list of options in the form of @var{key}=@var{value} pairs
> +separated by ":". A description of the accepted options follows.
> +
> + at table @option
> + at item luma_mode, l
> +
> +Available values are:
> +
> + at table @samp
> + at item none
> +Does nothing.

Do nothing.

> +
> + at item interleave, i
> +Interleave. Reverse the effect of deinterleave.
> +
> + at item deinterleave, d
> +Deinterleave line, placing one above the other.

Deinterleave lines

or:

Deinterleave fields, placing one above the other.

> + at end table
> +
> + at item chroma_mode, s
> +
> +Available values are:
> +
> + at table @samp
> + at item none
> +Does nothing.

Do nothing.

> +
> + at item interleave, i
> +Interleave. Reversing effect of deinterleave.
> +
> + at item deinterleave, d
> +Deinterleave line, placing one above the other.
> + at end table

Ditto, and you could factorize the tables.

@item luma_mode, l
@item chroma_mode, c

Available values for @option{luma_mode} and @option{chroma_mode} are:
...

> +
> + at item luma_swap
> +Swap luma fields. Exchange even & odd lines. Default value is @code{0}.
> +
> + at item chroma_swap
> +Swap chroma fields. Exchange even & odd lines. Default value is @code{0}.

Same here:

@item luma_swap
@item chroma_swap
Swap luma/chroma fields. Exchange even & odd lines. Default value is @code{0}.

> + at end table

Does it make sense to set luma != chroma? If not, we could
automatically set the values for chroma to luma, unless specified otherwise.

> +
>  @section thumbnail
>  Select the most representative frame in a given sequence of consecutive frames.
>  
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 938b183..67d829c 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -125,6 +125,7 @@ OBJS-$(CONFIG_HISTEQ_FILTER)                 += vf_histeq.o
>  OBJS-$(CONFIG_HQDN3D_FILTER)                 += vf_hqdn3d.o
>  OBJS-$(CONFIG_HUE_FILTER)                    += vf_hue.o
>  OBJS-$(CONFIG_IDET_FILTER)                   += vf_idet.o
> +OBJS-$(CONFIG_IL_FILTER)                     += vf_il.o
>  OBJS-$(CONFIG_KERNDEINT_FILTER)              += vf_kerndeint.o
>  OBJS-$(CONFIG_LUT_FILTER)                    += vf_lut.o
>  OBJS-$(CONFIG_LUTRGB_FILTER)                 += vf_lut.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 47158f9..cd65bab 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -119,6 +119,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER(HQDN3D,         hqdn3d,         vf);
>      REGISTER_FILTER(HUE,            hue,            vf);
>      REGISTER_FILTER(IDET,           idet,           vf);
> +    REGISTER_FILTER(IL,             il,             vf);
>      REGISTER_FILTER(KERNDEINT,      kerndeint,      vf);
>      REGISTER_FILTER(LUT,            lut,            vf);
>      REGISTER_FILTER(LUTRGB,         lutrgb,         vf);
> diff --git a/libavfilter/vf_il.c b/libavfilter/vf_il.c
> new file mode 100644
> index 0000000..3a28c2d
> --- /dev/null
> +++ b/libavfilter/vf_il.c
> @@ -0,0 +1,199 @@
> +/*
> + * Copyright (c) 2002 Michael Niedmayer
> + * Copyright (c) 2013 Paul B Mahol
> + *
> + * 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
> + * (de)interleave filter
> + */
> +
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "avfilter.h"
> +#include "internal.h"
> +
> +enum FilterMode {
> +    NONE,
> +    INTERLEAVE,
> +    DEINTERLEAVE
> +};

Nit: MODE_ for consistency/robustness.

> +
> +typedef struct {
> +    const AVClass *class;
> +    enum FilterMode luma_mode;
> +    int luma_swap;
> +    enum FilterMode chroma_mode;
> +    int chroma_swap;
> +    int nb_planes;
> +    int chroma_width, chroma_height;
> +    int width;
> +} IlContext;
> +
> +#define OFFSET(x) offsetof(IlContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption il_options[] = {
> +    {"luma_mode",   "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=NONE}, NONE, DEINTERLEAVE, FLAGS, "luma_mode"},
> +    {"l",           "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=NONE}, NONE, DEINTERLEAVE, FLAGS, "luma_mode"},
> +    {"none",         NULL, 0, AV_OPT_TYPE_CONST, {.i64=NONE},         0, 0, FLAGS, "luma_mode"},
> +    {"interleave",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE},   0, 0, FLAGS, "luma_mode"},
> +    {"i",            NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE},   0, 0, FLAGS, "luma_mode"},
> +    {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DEINTERLEAVE}, 0, 0, FLAGS, "luma_mode"},
> +    {"d",            NULL, 0, AV_OPT_TYPE_CONST, {.i64=DEINTERLEAVE}, 0, 0, FLAGS, "luma_mode"},
> +    {"chroma_mode", "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=NONE}, NONE, DEINTERLEAVE, FLAGS, "chroma_mode"},
> +    {"c",           "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=NONE}, NONE, DEINTERLEAVE, FLAGS, "chroma_mode"},
> +    {"none",         NULL, 0, AV_OPT_TYPE_CONST, {.i64=NONE},         0, 0, FLAGS, "chroma_mode"},
> +    {"interleave",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE},   0, 0, FLAGS, "chroma_mode"},
> +    {"i",            NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE},   0, 0, FLAGS, "chroma_mode"},
> +    {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DEINTERLEAVE}, 0, 0, FLAGS, "chroma_mode"},
> +    {"d",            NULL, 0, AV_OPT_TYPE_CONST, {.i64=DEINTERLEAVE}, 0, 0, FLAGS, "chroma_mode"},

the option descriptions may be filled

> +    {"luma_swap",   "swap luma fields",   OFFSET(luma_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
> +    {"chroma_swap", "swap chroma fields", OFFSET(chroma_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
> +    {NULL}
> +};
> +
> +AVFILTER_DEFINE_CLASS(il);
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args)
> +{
> +    IlContext *il = ctx->priv;
> +
> +    il->class = &il_class;
> +    av_opt_set_defaults(il);
> +
> +    return av_set_options_string(il, args, "=", ":");

technically wrong since this return the number of processed options,
and init() should return 0 in case of success, practically unrelevant
although
if ((ret = av_set_set_options_string()) < 0)
   return ret;
return 0;

might be safer.

> +}
> +
> +static int config_input(AVFilterLink *inlink)
> +{
> +    IlContext *il = inlink->dst->priv;
> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
> +    int i;
> +
> +    for (i = 0; i < desc->nb_components; i++)
> +        il->nb_planes = FFMAX(il->nb_planes, desc->comp[i].plane);
> +    il->nb_planes++;
> +
> +    il->chroma_height = inlink->h >> desc->log2_chroma_h;
> +    il->chroma_width  = inlink->w >> desc->log2_chroma_w;
> +    il->width = inlink->w;

> +    if (!(desc->flags & PIX_FMT_PLANAR))
> +        il->width *= av_get_bits_per_pixel(desc) >> 3;

The PIX_FMT_PLANAR flag is brain-dead and should be avoided (for
example this will fail with NV12). I can provide a function for
computing is_planar() if you want it.

Also, does it work if the pixel formats have padding?

> +
> +    return 0;
> +}
> +
> +static void interleave(uint8_t *dst, uint8_t *src, int w, int h,
> +                       int dstStride, int srcStride,

please avoid MPlayer-isms (dst/src_linesize is favored)

> +                       enum FilterMode mode, int swap)
> +{
> +    const int a = swap;
> +    const int b = 1 - a;
> +    const int m = h >> 1;
> +    int y;
> +
> +    switch (mode) {
> +    case DEINTERLEAVE:
> +        for (y = 0; y < m; y++) {
> +            memcpy(dst + dstStride *  y     , src + srcStride * (y * 2 + a), w);
> +            memcpy(dst + dstStride * (y + m), src + srcStride * (y * 2 + b), w);
> +        }
> +        break;
> +    case NONE:
> +        for (y = 0; y < m; y++) {
> +            memcpy(dst + dstStride *  y * 2     , src + srcStride * (y * 2 + a), w);
> +            memcpy(dst + dstStride * (y * 2 + 1), src + srcStride * (y * 2 + b), w);
> +        }
> +        break;
> +    case INTERLEAVE:
> +        for (y = 0; y < m; y++) {
> +            memcpy(dst + dstStride * (y * 2 + a), src + srcStride *  y     , w);
> +            memcpy(dst + dstStride * (y * 2 + b), src + srcStride * (y + m), w);
> +        }
> +        break;
> +    }
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref)
> +{
> +    IlContext *il = inlink->dst->priv;
> +    AVFilterLink *outlink = inlink->dst->outputs[0];
> +    AVFilterBufferRef *out;
> +    int ret;
> +
> +    out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
> +    if (!out) {
> +        avfilter_unref_bufferp(&inpicref);
> +        return AVERROR(ENOMEM);
> +    }
> +    out->pos = inpicref->pos;
> +    out->pts = inpicref->pts;
> +
> +    interleave(out->data[0], inpicref->data[0],
> +               il->width, inlink->h,
> +               out->linesize[0], inpicref->linesize[0],
> +               il->luma_mode, il->luma_swap);
> +
> +    if (il->nb_planes > 1) {
> +        interleave(out->data[1], inpicref->data[1],
> +                   il->chroma_width, il->chroma_height,
> +                   out->linesize[1], inpicref->linesize[1],
> +                   il->chroma_mode, il->chroma_swap);
> +        interleave(out->data[2], inpicref->data[2],
> +                   il->chroma_width, il->chroma_height,
> +                   out->linesize[2], inpicref->linesize[2],
> +                   il->chroma_mode, il->chroma_swap);
> +    }
> +
> +    ret = ff_filter_frame(outlink, out);
> +    avfilter_unref_bufferp(&inpicref);

> +    if (ret < 0)
> +        return ret;
> +    return 0;

return ret?

[...]
-- 
FFmpeg = Free and Faithful Marvellous Purposeless Extravagant Gorilla


More information about the ffmpeg-devel mailing list