[FFmpeg-devel] port mplayer eq filter to libavfilter

Stefano Sabatini stefano.sabatini-lala
Mon Nov 22 15:15:25 CET 2010


On date Monday 2010-11-22 20:33:11 +0800, William Yu encoded:
> I port mplayer's libmpcodec/vf_eq.c to libavfilter. May be some other
> people need it to adjust video's brightness and contrast

> diff --git a/Changelog b/Changelog
> index c713ce2..c8d09a7 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -57,6 +57,7 @@ version <next>:
>  - ASS subtitle encoder and decoder
>  - IEC 61937 encapsulation for E-AC3 (for HDMI passthrough)
>  - overlay filter added
> +- eq video filter added
>  
>  
>  version 0.6:
> diff --git a/configure b/configure
> index 7dcb50f..f599cec 100755
> --- a/configure
> +++ b/configure
> @@ -1408,6 +1408,7 @@ cropdetect_filter_deps="gpl"
>  frei0r_filter_deps="frei0r dlopen strtok_r"
>  ocv_smooth_filter_deps="libopencv"
>  yadif_filter_deps="gpl"
> +eq_filter_deps="gpl"

Nit: alphabetical order

>  
>  # libraries
>  avdevice_deps="avcodec avformat"
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 1cba2d6..44f5496 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -676,6 +676,41 @@ l.r     l.L
>  @end example
>  @end table
>  
> + at section eq
> +
> +Adjust brightness or contrast the input video.

of the input video.

> +
> +It accepts the following parameters:
> + at var{brightness}:@var{constrast}
> +
> +Negative values for the amount will less lightness or less contrast
> + the input video, while positive values will more lightness or more constrast.
> + All parameters are optional and default to the
> +equivalent of the string '0:0'.

Negative values for the amount will decrease the corresponding value
for the lightness or contrast of the input video, while positive
values will increase the corresponding value.

> +
> + at table @option
> +
> + at item brightness
> +Set the brightness amount. It can be an integer between -100
> +and 100, default value is 0.
> +
> + at item contrast
> +Set the contrast amount. It can be an integer between -100
> +and 100, default value is 0.
> +
> + at end table
> +
> + at example
> +# 20 percent light and 20 percent contrast

increase lightness and contrast by 20 percent
> +eq=20:20
> +

> +# 20 percent dark and 20 percent diffusion

decrease ...
> +eq=-20:-20
> +

> +# Use the default values with @command{ffmpeg}
> +./ffmpeg -i in.avi -vf "eq" out.mp4

This example can be skipped.
> + at end example

> +
>  @section unsharp
>  
>  Sharpen or blur the input video.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 56c95df..38f119d 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -43,6 +43,7 @@ OBJS-$(CONFIG_TRANSPOSE_FILTER)              += vf_transpose.o
>  OBJS-$(CONFIG_UNSHARP_FILTER)                += vf_unsharp.o
>  OBJS-$(CONFIG_VFLIP_FILTER)                  += vf_vflip.o
>  OBJS-$(CONFIG_YADIF_FILTER)                  += vf_yadif.o
> +OBJS-$(CONFIG_EQ_FILTER)                     += vf_eq.o
>  
>  OBJS-$(CONFIG_BUFFER_FILTER)                 += vsrc_buffer.o
>  OBJS-$(CONFIG_COLOR_FILTER)                  += vf_pad.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 0cac9ba..0b09cdc 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -64,6 +64,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER (UNSHARP,     unsharp,     vf);
>      REGISTER_FILTER (VFLIP,       vflip,       vf);
>      REGISTER_FILTER (YADIF,       yadif,       vf);
> +    REGISTER_FILTER (EQ,          eq,          vf);
>  
>      REGISTER_FILTER (BUFFER,      buffer,      vsrc);
>      REGISTER_FILTER (COLOR,       color,       vsrc);
> diff --git a/libavfilter/eq.h b/libavfilter/eq.h
> new file mode 100644
> index 0000000..7084e5c
> --- /dev/null
> +++ b/libavfilter/eq.h
> @@ -0,0 +1,34 @@
> +/*

> + * Ported to FFmpeg from MPlayer libmpcodecs/vf_eq.c

This can go to the @file description, and I believe Richard Felker is
the holder of the original copyright (if you care ask him if he's OK
with relicensing it to LGPL).

> + * Port copyright (C) 2010 William Yu <genwillyu at gmail dot com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */

nit: use @file for describing the file

For this file I believe it's OK to use LGPL.

> +
> +#ifndef AVFILTER_EQ_H
> +#define AVFILTER_EQ_H
> +
> +#include "avfilter.h"
> +
> +#if HAVE_MMX
> +void ff_eq_filter_process_mmx(uint8_t *dest,
> +                              int dstride, uint8_t *src, int sstride,
> +                              int w, int h, int brightness,
> +                              int contrast);
> +#endif /* HAVE_MMX */
> +
> +#endif /* AVFILTER_EQ_H */
> diff --git a/libavfilter/vf_eq.c b/libavfilter/vf_eq.c
> new file mode 100644
> index 0000000..0c25a8e
> --- /dev/null
> +++ b/libavfilter/vf_eq.c
> @@ -0,0 +1,181 @@
> +/*
> + * Ported to FFmpeg from MPlayer libmpcodecs/vf_eq.c
> + * Port copyright (C) 2010 William Yu <genwillyu at gmail dot com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "avfilter.h"
> +#include "libavutil/cpu.h"
> +#include "libavutil/common.h"
> +#include "libavutil/pixdesc.h"
> +#include "eq.h"

nit++:
#include "libavutil/cpu.h"
#include "libavutil/common.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "eq.h"

that is order should be:
system headers
non-libavfilter FFmpeg headers (libavutil, libavcore in this order)
libavfilter headers

> +
> +#define CHROMA_WIDTH(link)  -((-link->w) >> av_pix_fmt_descriptors[link->format].log2_chroma_w)
> +#define CHROMA_HEIGHT(link) -((-link->h) >> av_pix_fmt_descriptors[link->format].log2_chroma_h)
> +
> +typedef struct EQContext {
> +    int brightness;         ///< scale from -100 to 100
> +    int contrast;           ///< scale from -100 to 100
> +    void (*process)(        ///< process function
> +        unsigned char *dest,
> +        int dstride,
> +        unsigned char *src,
> +        int sstride,
> +        int w, int h,
> +        int brightness,
> +        int contrast);
> +}  EQContext;
> +
> +static void process_c(unsigned char *dest, int dstride, unsigned char *src, int sstride,

nit: dest -> dst

Also
dstride -> dst_stride (or dst_linesize)
sstride -> src_stride (or src_linesize)

should be more readable.

> +            int w, int h, int brightness, int contrast)
> +{
> +    int i;
> +    int pel;
> +    int dstep = dstride-w;
> +    int sstep = sstride-w;
> +
> +    contrast = ((contrast+100)*256*256)/100;
> +    brightness = ((brightness+100)*511)/200-128 - (contrast>>9);
> +
> +    while (h--) {
> +        for (i = w; i; i--) {
> +            pel = ((*src++* contrast)>>16) + brightness;
> +            if (pel&768) pel = (-pel)>>31;
> +            *dest++ = pel;
> +        }
> +        src += sstep;
> +        dest += dstep;
> +    }
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    enum PixelFormat pix_fmts[] = {
> +        PIX_FMT_YUV420P,  PIX_FMT_YUV422P,  PIX_FMT_YUV444P,  PIX_FMT_YUV410P,
> +        PIX_FMT_YUV411P,  PIX_FMT_YUV440P,  PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P,
> +        PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P, PIX_FMT_NONE
> +    };
> +
> +    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
> +
> +    return 0;
> +}
> +
> +static av_cold int init(AVFilterContext *ctx, const char * args, void * opaque)
> +{
> +    EQContext * eq = ctx->priv;
> +    av_unused int cpu_flags = av_get_cpu_flags();
> +
> +    eq->brightness = 0;
> +    eq->contrast = 0;
> +
> +    if (args)
> +        sscanf(args, "%d:%d", &(eq->brightness), &(eq->contrast));
> +
> +    if (eq->brightness < -100 || eq->brightness > 100 ||
> +        eq->contrast < -100 || eq->contrast > 100) {
> +        av_log(ctx, AV_LOG_ERROR,
> +                "Invalid brightness or contrast value %d:%d\n",
> +                eq->brightness, eq->contrast);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    av_log(ctx, AV_LOG_INFO,
> +        "brightness and contrast value %d:%d\n",
> +        eq->brightness, eq->contrast);
> +    eq->process = process_c;
> +#if HAVE_MMX
> +    if (HAVE_MMX && cpu_flags & AV_CPU_FLAG_MMX) {
> +        av_log(ctx,AV_LOG_INFO,"use mmx\n");
> +        eq->process = ff_eq_filter_process_mmx;
> +    }
> +#endif
> +
> +    return 0;
> +}
> +

> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +}

unnecessary

> +
> +static void copy_chroma(
> +    AVFilterBufferRef *in,
> +    AVFilterBufferRef *out,
> +    AVFilterLink *link)
> +{
> +    int w = CHROMA_WIDTH(link);
> +    int h = CHROMA_HEIGHT(link);
> +    int y;

> +    uint8_t * src = in->data[1];
> +    uint8_t * dst = out->data[1];

nit: *src, *dst

> +
> +    if (out->linesize[1] == in->linesize[1] ) {
> +        memcpy( dst, src, in->linesize[1]*h);
> +    } else {
> +        for (y=0; y < h; y++, dst += out->linesize[1], src += in->linesize[1])
> +            memcpy(dst, src, w);
> +    }
> +
> +    src = in->data[2];
> +    dst = out->data[2];
> +    if (out->linesize[2] == in->linesize[2] ) {
> +        memcpy( dst, src, in->linesize[2]*h);
> +    } else {
> +        for (y=0; y < h; y++, dst += out->linesize[2], src += in->linesize[2])
> +            memcpy(dst, src, w);
> +    }

duplicated code (for plane = 1; plane < 2; plane++)
also check imgutils.c:av_image_copy_plane(), also maybe this code
can be moved directly in end_frame()
 
> +}
> +
> +static void end_frame(AVFilterLink *link)

inlink for enhanced readability

> +{
> +    EQContext * eq = link->dst->priv;
> +    AVFilterBufferRef *in  = link->cur_buf;
> +    AVFilterBufferRef *out = link->dst->outputs[0]->out_buf;
> +
> +    eq->process(out->data[0], out->linesize[0],
> +        in->data[0], in->linesize[0],
> +        link->w, link->h, eq->brightness, eq->contrast);
> +    copy_chroma(in,out,link);
> +
> +    avfilter_unref_buffer(in);
> +    avfilter_draw_slice(link->dst->outputs[0], 0, link->h, 1);
> +    avfilter_end_frame(link->dst->outputs[0]);
> +    avfilter_unref_buffer(out);
> +}
> +

> +static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
> +{
> +}

call it null_draw_slice

> +AVFilter avfilter_vf_eq = {
> +    .name   = "eq",
> +    .description = NULL_IF_CONFIG_SMALL("Video equalizer."),
> +    .priv_size = sizeof(EQContext),
> +    .init = init,
> +    .uninit = uninit,
> +    .query_formats = query_formats,
> +    .inputs     = (AVFilterPad[]) {{ .name          = "default",
> +                                     .type          = AVMEDIA_TYPE_VIDEO,
> +                                     .draw_slice    = draw_slice,
> +                                     .end_frame     = end_frame,
> +                                     .min_perms     = AV_PERM_READ, },
> +                                   { .name = NULL}},
> +    .outputs    = (AVFilterPad[]) {{ .name      = "default",
> +                                     .type      = AVMEDIA_TYPE_VIDEO, },
> +                                   { .name = NULL}},
> +    };
> diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile
> index 716048c..bb9d76d 100644
> --- a/libavfilter/x86/Makefile
> +++ b/libavfilter/x86/Makefile
> @@ -1 +1,2 @@
>  MMX-OBJS-$(CONFIG_YADIF_FILTER)              += x86/yadif.o
> +MMX-OBJS-$(CONFIG_EQ_FILTER)                 += x86/eq.o
> diff --git a/libavfilter/x86/eq.c b/libavfilter/x86/eq.c
> new file mode 100644
> index 0000000..6e3fac2
> --- /dev/null
> +++ b/libavfilter/x86/eq.c
> @@ -0,0 +1,78 @@
> +/*
> + * Ported to FFmpeg from MPlayer libmpcodecs/vf_eq.c
> + * Port copyright (C) 2010 William Yu <genwillyu at gmail dot com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavfilter/eq.h"
> +
> +void ff_eq_filter_process_mmx(uint8_t *dest, int dstride, uint8_t *src, int sstride, int w, int h, int brightness, int contrast)
> +{
> +    int i;
> +    int pel;
> +    int dstep = dstride-w;
> +    int sstep = sstride-w;
> +    short brvec[4];
> +    short contvec[4];
> +
> +    contrast = ((contrast+100)*256*16)/100;
> +    brightness = ((brightness+100)*511)/200-128 - (contrast>>5);
> +
> +    brvec[0] = brvec[1] = brvec[2] = brvec[3] = brightness;
> +    contvec[0] = contvec[1] = contvec[2] = contvec[3] = contrast;
> +
> +    while (h--) {
> +        __asm__ volatile (
> +            "movq (%5), %%mm3 \n\t"
> +            "movq (%6), %%mm4 \n\t"
> +            "pxor %%mm0, %%mm0 \n\t"
> +            "movl %4, %%eax\n\t"
> +            ASMALIGN(4)
> +            "1: \n\t"
> +            "movq (%0), %%mm1 \n\t"
> +            "movq (%0), %%mm2 \n\t"
> +            "punpcklbw %%mm0, %%mm1 \n\t"
> +            "punpckhbw %%mm0, %%mm2 \n\t"
> +            "psllw $4, %%mm1 \n\t"
> +            "psllw $4, %%mm2 \n\t"
> +            "pmulhw %%mm4, %%mm1 \n\t"
> +            "pmulhw %%mm4, %%mm2 \n\t"
> +            "paddw %%mm3, %%mm1 \n\t"
> +            "paddw %%mm3, %%mm2 \n\t"
> +            "packuswb %%mm2, %%mm1 \n\t"
> +            "add $8, %0 \n\t"
> +            "movq %%mm1, (%1) \n\t"
> +            "add $8, %1 \n\t"
> +            "decl %%eax \n\t"
> +            "jnz 1b \n\t"
> +            : "=r" (src), "=r" (dest)
> +            : "0" (src), "1" (dest), "r" (w>>3), "r" (brvec), "r" (contvec)
> +            : "%eax"
> +        );
> +
> +        for (i = w&7; i; i--) {
> +            pel = ((*src++* contrast)>>12) + brightness;
> +            if (pel&768) pel = (-pel)>>31;
> +            *dest++ = pel;
> +        }
> +
> +        src += sstep;
> +        dest += dstep;
> +    }
> +    __asm__ volatile ( "emms \n\t" ::: "memory" );
> +}

Thanks for the patch.
-- 
FFmpeg = Forgiving & Faithless Murdering Peaceless Enhanced God



More information about the ffmpeg-devel mailing list