[FFmpeg-devel] [PATCH] Port mp=eq/eq2 to FFmpeg

Stefano Sabatini stefasab at gmail.com
Wed Jan 21 10:36:45 CET 2015


On date Tuesday 2015-01-20 22:30:54 +0530, Arwa Arif encoded:
[...]
> > Add also an entry to add support to .process_command(). You can
> > implement it in a later patch.
> >
> >
> What is meant by adding support to process_command?

grep for .process_command in the libavfilter dir, and you will know. 
eq is meant to be used interactively, that's why having a
process_command callback is particuarly useful in this case.

[...]
> > > +static void set_saturation(EQ2Context *eq2)
> > > +{
> > > +    int i;
> > > +    /* saturation already set as AVOpt */
> > > +
> > > +    for (i = 1; i < 3; i++) {
> > > +        eq2->param[i].contrast = eq2->saturation;
> > > +        eq2->param[i].lut_clean = 0;
> > > +        check_values(&eq2->param[i]);
> > > +    }
> >
> > Is this really working with gray8 or crashing (like mp=eq does)?
> >
> 
> Yes, I checked the formats, it is working with gray8.
> 
> > You should store in the context the number of planes.
> >
> > Also, please add a fate test.
> >
> 

> I am not able to run rsync, maybe because of proxy settings. I tried
> various ways of bypassing the proxy, but I am still unable to use it. Is
> there any other way of adding the fate test?

You don't need to have the complete fate repository to run the
test. Give me some time and try to come with a patch (it's been a
while I'm not hacking in tests, and things moved).
 
> Updated the patch.

> From b54fd33b1d4ad68487ce20480c7865ad95ac19d8 Mon Sep 17 00:00:00 2001
> From: Arwa Arif <arwaarif1994 at gmail.com>
> Date: Mon, 19 Jan 2015 03:56:48 +0530

> Subject: [PATCH] Port mp=eq/eq2 to FFmpeg Code

> adapted from James Darnley's
>  previous commits

This can stay in a separate line.

> 
> ---
>  configure                |    2 +
>  doc/filters.texi         |   65 ++++++++++
>  libavfilter/Makefile     |    2 +
>  libavfilter/allfilters.c |    2 +
>  libavfilter/vf_eq.c      |  325 ++++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/vf_eq.h      |   63 +++++++++
>  libavfilter/x86/Makefile |    1 +
>  libavfilter/x86/vf_eq.c  |   94 ++++++++++++++
>  8 files changed, 554 insertions(+)
>  create mode 100644 libavfilter/vf_eq.c
>  create mode 100644 libavfilter/vf_eq.h
>  create mode 100644 libavfilter/x86/vf_eq.c
> 
> diff --git a/configure b/configure
> index c73562b..a8042b2 100755
> --- a/configure
> +++ b/configure
> @@ -2579,6 +2579,8 @@ delogo_filter_deps="gpl"
>  deshake_filter_select="pixelutils"
>  drawtext_filter_deps="libfreetype"
>  ebur128_filter_deps="gpl"
> +eq_filter_deps="gpl"
> +eq2_filter_deps="gpl"
>  flite_filter_deps="libflite"
>  frei0r_filter_deps="frei0r dlopen"
>  frei0r_src_filter_deps="frei0r dlopen"
> diff --git a/doc/filters.texi b/doc/filters.texi
> index d7b2273..ded154a 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -4320,6 +4320,71 @@ edgedetect=mode=colormix:high=0
>  @end example
>  @end itemize
>  
> + at anchor{eq}
> + at section eq
> +Control brightness and contrast. It can be used for fixing poorly captured movies,
> +or for slightly reducing contrast to mask artifacts and get by with lower bitrates.
> +
> +The filter accepts the following options:
> +
> + at table @option
> +
> + at item brightness
> +Set the brightness value. It accepts a float value in range @code{-1.0} to
> + at code{1.0}. The default value is @code{0.0}.
> +
> + at item contrast
> +Set the contrast value. It accepts a float value in range @code{-1.0} to
> + at code{1.0}. The default value is @code{0.0}.
> + at end table
> +
> + at section eq2
> +Equalizer that uses lookup tables (very slow), allowing gamma correction
> +in addition to simple brightness and contrast adjustment.
> +
> +Note that it uses the same optimized code as @ref{eq} if all gamma values
> +are 1.0. The parameters are given as floating point values.
> +
> +The filter accepts the following options:
> +
> + at table @option
> + at item brightness
> +Set the brightness value. It accepts a float value in range @code{-1.0} to
> + at code{1.0}. The default value is @code{0.0}.
> +
> + at item contrast
> +Set the contrast value. It accepts a float value in range @code{-2.0} to
> + at code{2.0}. The default value is @code{0.0}.
> +
> + at item gamma
> +Set the gamma value. It accepts a float value in range @code{0.1} to @code{10.0}.
> +The default value is @code{1.0}.
> +
> + at item gamma_y
> +Set the gamma value for the luma plane. It accepts a float value in range
> + at code{0.1} to @code{10.0}. The default value is @code{1.0}.
> +
> + at item gamma_u
> +Set the gamma value for 1st chroma plane. It accepts a float value in range
> + at code{0.1} to @code{10.0}. The default value is @code{1.0}.
> +
> + at item gamma_v
> +Set the gamma value for 2nd chroma plane. It accepts a float value in range
> + at code{0.1} to @code{10.0}. The default value is @code{1.0}.
> +
> + at item saturation
> +Set the saturation value. It accepts a float value in range @code{0.0} to
> + at code{3.0}. The default value is @code{1.0}.
> +
> + at item weight
> +Can be used to reduce the effect of a high gamma value on bright image areas,
> +e.g. keep them from getting overamplified and just plain white. It accepts a
> +float value in range @code{0.0} to @code{1.0}.A value of @code{0.0} turns the
> +gamma correction all the way down while @code{1.0} leaves it at its full strength.
> +Default is @code{1.0}.
> +
> + at end table
> +
>  @section extractplanes
>  
>  Extract color channel components from input video stream into
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index e43d76d..8dab587 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -116,6 +116,8 @@ OBJS-$(CONFIG_DRAWGRID_FILTER)               += vf_drawbox.o
>  OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
>  OBJS-$(CONFIG_ELBG_FILTER)                   += vf_elbg.o
>  OBJS-$(CONFIG_EDGEDETECT_FILTER)             += vf_edgedetect.o
> +OBJS-$(CONFIG_EQ_FILTER)                     += vf_eq.o
> +OBJS-$(CONFIG_EQ2_FILTER)                    += vf_eq.o
>  OBJS-$(CONFIG_EXTRACTPLANES_FILTER)          += vf_extractplanes.o
>  OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
>  OBJS-$(CONFIG_FIELD_FILTER)                  += vf_field.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 381da4f..aa92449 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -132,6 +132,8 @@ void avfilter_register_all(void)
>      REGISTER_FILTER(DRAWTEXT,       drawtext,       vf);
>      REGISTER_FILTER(EDGEDETECT,     edgedetect,     vf);
>      REGISTER_FILTER(ELBG,           elbg,           vf);
> +    REGISTER_FILTER(EQ,             eq,             vf);
> +    REGISTER_FILTER(EQ2,            eq2,            vf);
>      REGISTER_FILTER(EXTRACTPLANES,  extractplanes,  vf);
>      REGISTER_FILTER(FADE,           fade,           vf);
>      REGISTER_FILTER(FIELD,          field,          vf);
> diff --git a/libavfilter/vf_eq.c b/libavfilter/vf_eq.c
> new file mode 100644
> index 0000000..a6bee7d
> --- /dev/null
> +++ b/libavfilter/vf_eq.c
> @@ -0,0 +1,325 @@
> +/*
> + * Original  MPlayer filters by Richard Felker, Hampa Hug, Daniel Moreno,
> + * and Michael Niedermeyer.
> + *
> + * Copyright (c) 2014 James Darnley <james.darnley at gmail.com>
> + * Copyright (c) 2015 Arwa Arif <arwaarif1994 at 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 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.
> + */
> +
> +/**
> + * @file
> + * very simple video equalizer
> + */
> +
> +#include "libavfilter/internal.h"
> +#include "libavutil/common.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "vf_eq.h"
> +
> +AVFilter ff_vf_eq, ff_vf_eq2;
> +
> +#define IS_FILTER_EQ(x) ((x) == &ff_vf_eq)
> +
> +static void create_lut(EQ2Parameters *param)
> +{
> +    int i;
> +    double g = param->gamma;
> +
> +    g = 1.0 / g;
> +
> +    for (i = 0; i < 256; i++) {
> +        double v = i / 255.0;
> +        v = param->contrast * (v - 0.5) + 0.5 + param->brightness;
> +
> +        if (v <= 0.0)
> +            param->lut[i] = 0;
> +        else {
> +            v = v * (1.0 - param->weight) + pow(v, g) * param->weight;
> +
> +            if (v >= 1.0)
> +                param->lut[i] = 255;
> +            else
> +                param->lut[i] = 256.0 * v;
> +        }
> +    }
> +
> +    param->lut_clean = 1;
> +}
> +
> +static void apply_lut(EQ2Parameters *param, uint8_t *dst, int dst_stride,
> +                      uint8_t *src, int src_stride, int w, int h)
> +{
> +    int x, y;
> +
> +    if (!param->lut_clean)
> +        create_lut(param);
> +
> +    for (y = 0; y < h; y++) {
> +        for (x = 0; x < w; x++) {
> +            dst[y*dst_stride+x] = param->lut[src[y*src_stride+x]];
> +        }
> +    }
> +}
> +
> +static void process_c(EQ2Parameters *param, uint8_t *dst, int dst_stride,
> +                      uint8_t *src, int src_stride, int w, int h)
> +{
> +    int x, y, pel;
> +
> +    for (y = 0; y < h; y++) {
> +        for (x = 0; x < w; x++) {
> +            pel = ((src[y * src_stride + x] * param->c) >> 16) + param->b;
> +
> +            if (pel & 768)
> +                pel = (-pel) >> 31;
> +
> +            dst[y * dst_stride + x] = pel;
> +        }
> +    }
> +}
> +
> +static void check_values(EQ2Parameters *param, EQ2Context *eq2)
> +{
> +    if (param->contrast == 1.0 && param->brightness == 0.0 && param->gamma == 1.0)
> +        param->adjust = NULL;
> +    else if (param->gamma == 1.0)
> +        param->adjust = eq2->process;
> +    else
> +        param->adjust = apply_lut;
> +}
> +
> +static void set_contrast(EQ2Context *eq2)
> +{
> +    eq2->param[0].contrast = eq2->contrast;
> +    eq2->param[0].lut_clean = 0;
> +    check_values(&eq2->param[0], eq2);
> +}
> +
> +static void set_brightness(EQ2Context *eq2)
> +{
> +    eq2->param[0].brightness = eq2->brightness;
> +    eq2->param[0].lut_clean = 0;
> +    check_values(&eq2->param[0], eq2);
> +}
> +
> +static void set_gamma(EQ2Context *eq2)
> +{
> +    int i;
> +    eq2->param[0].gamma = eq2->gamma * eq2->gamma_u;
> +    eq2->param[1].gamma = sqrt(eq2->gamma_y / eq2->gamma_u);
> +    eq2->param[2].gamma = sqrt(eq2->gamma_v / eq2->gamma_u);
> +
> +    for (i = 0; i < 3; i++) {
> +        eq2->param[i].weight = eq2->weight;
> +        eq2->param[i].lut_clean = 0;
> +        check_values(&eq2->param[i], eq2);
> +    }
> +}
> +
> +static void set_saturation(EQ2Context *eq2)
> +{
> +    int i;
> +    for (i = 1; i < 3; i++) {
> +        eq2->param[i].contrast = eq2->saturation;
> +        eq2->param[i].lut_clean = 0;
> +        check_values(&eq2->param[i], eq2);
> +    }
> +}
> +
> +static int initialize(AVFilterContext *ctx)
> +{
> +    EQ2Context *eq2 = ctx->priv;
> +    int i;
> +
> +    set_gamma(eq2);
> +    set_contrast(eq2);
> +    set_brightness(eq2);
> +    set_saturation(eq2);
> +
> +    if (IS_FILTER_EQ(ctx->filter)) {
> +        eq2->param[0].contrast += 1.0;
> +        eq2->param[1].adjust = NULL;
> +        eq2->param[2].adjust = NULL;
> +    }
> +
> +    for (i = 0; i < 3; i++) {
> +        eq2->param[i].c = (eq2->param[i].contrast) * 65536.0;
> +        eq2->param[i].b = (eq2->param[i].brightness + 1.0) * 255.5 - 128.0 - (eq2->param[i].contrast) * 128.0;
> +    }
> +
> +    eq2->process = process_c;
> +
> +    if (ARCH_X86)
> +    {
> +        ff_eq_init_x86(eq2);
> +    }
> +
> +    return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum AVPixelFormat pixel_fmts_eq[] = {
> +        AV_PIX_FMT_GRAY8,
> +        AV_PIX_FMT_NV12,
> +        AV_PIX_FMT_NV21,
> +        AV_PIX_FMT_YUV410P,
> +        AV_PIX_FMT_YUV411P,
> +        AV_PIX_FMT_YUV420P,
> +        AV_PIX_FMT_YUV422P,
> +        AV_PIX_FMT_YUV444P,
> +        AV_PIX_FMT_NONE
> +    };
> +
> +    static const enum AVPixelFormat pixel_fmts_eq2[] = {
> +        AV_PIX_FMT_GRAY8,
> +        AV_PIX_FMT_YUV410P,
> +        AV_PIX_FMT_YUV411P,
> +        AV_PIX_FMT_YUV420P,
> +        AV_PIX_FMT_YUV422P,
> +        AV_PIX_FMT_YUV444P,
> +        AV_PIX_FMT_NONE
> +    };
> +
> +    if (IS_FILTER_EQ(ctx->filter))
> +        ff_set_common_formats(ctx, ff_make_format_list(pixel_fmts_eq));
> +    else
> +        ff_set_common_formats(ctx, ff_make_format_list(pixel_fmts_eq2));
> +
> +    return 0;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    AVFilterLink *outlink = inlink->dst->outputs[0];
> +    EQ2Context *eq2 = ctx->priv;
> +    AVFrame *out;
> +    const AVPixFmtDescriptor *desc;
> +    int i;
> +
> +    out = ff_get_video_buffer(outlink, inlink->w, inlink->h);
> +    if (!out)
> +        return AVERROR(ENOMEM);
> +
> +    av_frame_copy_props(out, in);
> +    desc = av_pix_fmt_desc_get(inlink->format);
> +
> +    for (i = 0; i < desc->nb_components; i++) {
> +        int w = inlink->w;
> +        int h = inlink->h;
> +
> +        if (i == 1 || i == 2) {
> +            w = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
> +            h = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
> +        }
> +

> +        if (eq2->param[i].adjust)
> +            eq2->param[i].adjust(&eq2->param[i], out->data[i], out->linesize[i],
> +                                 in->data[i], in->linesize[i], w, h);
> +        else
> +            av_image_copy_plane(out->data[i], out->linesize[i],
> +                                in->data[i], in->linesize[i], w, h);
> +    }

I still expect that eq and eq2 should have the same performances,
since the adjust callback is set depending on the parameter values. So
we should have a single eq filter.

Please investigate about why you get different benchmark values.
-- 
FFmpeg = Fancy Fancy Multipurpose Pacific Elitist Game


More information about the ffmpeg-devel mailing list