On date Monday 2015-01-19 04:04:45 +0530, Arwa Arif encoded: > Attached the patch.
> From 79298b4f6d08abacb387dbd3f75fabe329d96772 Mon Sep 17 00:00:00 2001 > From: Arwa Arif <arwaarif1...@gmail.com> > Date: Mon, 19 Jan 2015 03:56:48 +0530 > Subject: [PATCH] Port mp=eq/eq2 to FFmpeg Mention James Darnley in the log message. Something like: this code is adapted from a port by James Darnley. > > --- > configure | 2 + > doc/filters.texi | 68 +++++++++ > libavfilter/Makefile | 2 + > libavfilter/allfilters.c | 2 + > libavfilter/vf_eq.c | 342 > ++++++++++++++++++++++++++++++++++++++++++++++ > libavfilter/vf_eq.h | 63 +++++++++ > libavfilter/x86/Makefile | 1 + > libavfilter/x86/vf_eq.c | 94 +++++++++++++ > 8 files changed, 574 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..5eff0b5 100644 > --- a/doc/filters.texi > +++ b/doc/filters.texi > @@ -4320,6 +4320,74 @@ edgedetect=mode=colormix:high=0 > @end example > @end itemize > > +@anchor{eq} > +@section eq > +Software equalizer with interactive controls just like the hardware > equalizer, > +for cards/drivers that do not support brightness and contrast controls in > hardware. Drop the redundant reference to software/hardware (this is obviously a software equalizer). Something like: Control brightness and contrast. should be enough. > + > +Might also be useful with MEncoder, either for fixing poorly captured > movies, or > +for slightly reducing contrast to mask artifacts and get by with lower > bitrates. Drop the reference to MEncoder. > + > +The filter accepts the following options: > + > +@table @option > + > +@item brightness > +Set the brightness value. It accepts a float value in range @code{-1.0} to > +@code{1.0}. The default value is @code{0.0}. > + > +@item contrast > +Set the contrast value. It accepts a float value in range @code{-1.0} to > +@code{1.0}. The default value is @code{0.0}. > +@end table > + > +@section eq2 > +Alternative software equalizer that uses lookup tables (very slow), allowing > +gamma correction in addition to simple brightness and contrast adjustment. > + > +Note that it uses the same MMX optimized code as @ref{eq} if all gamma values Note that it uses the same optimizied code ... > +are 1.0. The parameters are given as floating point values. I wonder if it still makes sense to get two distinct filters. Can you show benhmarks? > + > +The filter accepts the following options: > + > +@table @option > +@item brightness > +Set the brightness value. It accepts a float value in range @code{-1.0} to > +@code{1.0}. The default value is @code{0.0}. > + > +@item contrast > +Set the contrast value. It accepts a float value in range @code{-2.0} to > +@code{2.0}. The default value is @code{0.0}. > + > +@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}. > + > +@item gamma_b > +Set the gamma value for blue component. It accepts a float value in range > +@code{0.1} to @code{10.0}. The default value is @code{1.0}. > + > +@item gamma_g > +Set the gamma value for green component. It accepts a float value in range > +@code{0.1} to @code{10.0}. The default value is @code{1.0}. > + > +@item gamma_r > +Set the gamma value for red component. It accepts a float value in range > +@code{0.1} to @code{10.0}. The default value is @code{1.0}. > + > +@item saturation > +Set the saturation value. It accepts a float value in range @code{0.0} to > +@code{3.0}. The default value is @code{1.0}. > + > +@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}. > + > +@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..c126c23 > --- /dev/null > +++ b/libavfilter/vf_eq.c > @@ -0,0 +1,342 @@ > +/* > + * Original MPlayer filters by Richard Felker, Hampa Hug, Daniel Moreno, > + * and Michael Niedermeyer. > + * > + * Copyright (c) 2014 James Darnley <james.darn...@gmail.com> > + * Copyright (c) 2015 Arwa Arif <arwaarif1...@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 > + */ > + > +/* TODO: > + * - copy plane pointers rather than data uh? drop this comment unless it is clear to you what it means > + * - support alpha channels Add also an entry to add support to .process_command(). You can implement it in a later patch. > + */ > + > +#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; > + > + if (g < 0.001 || g > 1000.0) > + g = 1.0; is this check useful (considering that we limit the option value through min/max)? > + > + 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) > +{ > + if (param->contrast == 1.0 && param->brightness == 0.0 && param->gamma > == 1.0) > + param->adjust = NULL; > + else if (param->gamma == 1.0) > + param->adjust = param->process; > + else > + param->adjust = apply_lut; > +} > + > +static void set_contrast(EQ2Context *eq2) > +{ > + /* contrast already set as AVOpt */ drop these comments, they are useless and confusing > + > + eq2->param[0].contrast = eq2->contrast; > + eq2->param[0].lut_clean = 0; > + check_values(&eq2->param[0]); > +} > + > +static void set_brightness(EQ2Context *eq2) > +{ > + /* brightness already set as AVOpt */ > + > + eq2->param[0].brightness = eq2->brightness; > + eq2->param[0].lut_clean = 0; > + check_values(&eq2->param[0]); > +} > + > +static void set_gamma(EQ2Context *eq2) > +{ > + int i; > + /* gamma already set as AVOpt */ > + > + eq2->param[0].gamma = eq2->gamma * eq2->gamma_g; > + eq2->param[1].gamma = sqrt(eq2->gamma_b / eq2->gamma_g); > + eq2->param[2].gamma = sqrt(eq2->gamma_r / eq2->gamma_g); > + > + for (i = 0; i < 3; i++) { > + eq2->param[i].weight = eq2->weight; > + eq2->param[i].lut_clean = 0; > + check_values(&eq2->param[i]); > + } > +} > + > +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)? You should store in the context the number of planes. Also, please add a fate test. > +} > + > +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->param->process = process_c; > + > + if (ARCH_X86) > + { > + ff_eq_init_x86; > + } > + > + return 0; > +} > + > +static int query_formats(AVFilterContext *ctx) > +{ > + static const enum AVPixelFormat pixel_fmts_eq[] = { > + /* Only IMGFMT_CLPL is missing. */ what's this? > + 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 *csp; meaningful name please > + int i; > + > + out = ff_get_video_buffer(outlink, inlink->w, inlink->h); > + if (!out) > + return AVERROR(ENOMEM); > + > + av_frame_copy_props(out, in); > + csp = av_pix_fmt_desc_get(inlink->format); > + > + for (i = 0; i < csp->nb_components; i++) { > + int w = inlink->w; > + int h = inlink->h; > + > + if (i == 1 || i == 2) { > + w = FF_CEIL_RSHIFT(w, csp->log2_chroma_w); > + h = FF_CEIL_RSHIFT(h, csp->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); > + } > + > + if (in != out) { > + if (in->data[3]) > + av_image_copy_plane(out->data[3], out->linesize[3], > + in ->data[3], in ->linesize[3], > + inlink->w, inlink->h); > + av_frame_free(&in); > + } > + > + return ff_filter_frame(outlink, out); > +} > +static const AVFilterPad eq_inputs[] = { > + { > + .name = "default", > + .type = AVMEDIA_TYPE_VIDEO, > + .filter_frame = filter_frame, > + }, > + { NULL } > +}; > + > +static const AVFilterPad eq_outputs[] = { > + { > + .name = "default", > + .type = AVMEDIA_TYPE_VIDEO, > + }, > + { NULL } > +}; > + > +#define OFFSET(x) offsetof(EQ2Context, x) > +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM > + > +static const AVOption eq_options[] = { > + { "brightness", "set the brightness adjustment", > + OFFSET(brightness), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -1.0, 1.0, FLAGS > }, > + { "contrast", "set the contrast adjustment", > + OFFSET(contrast), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -1.0, 1.0, FLAGS > }, > + { NULL } > +}; > + > +AVFILTER_DEFINE_CLASS(eq); > + > +static const AVOption eq2_options[] = { > + { "brightness", "set the brightness adjustment", > + OFFSET(brightness), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, -1.0, 1.0, > FLAGS }, > + { "contrast", "set the contrast adjustment, negative values give a > negative image", > + OFFSET(contrast), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, -2.0, 2.0, > FLAGS }, > + { "gamma", "set the initial gamma value", > + OFFSET(gamma), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0.1, 10.0, > FLAGS }, > + { "gamma_b", "gamma value for the luma plane", > + OFFSET(gamma_b), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0.1, 10.0, > FLAGS }, > + { "gamma_g", "gamma value for the 1st chroma plane", > + OFFSET(gamma_g), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0.1, 10.0, > FLAGS }, > + { "gamma_r", "gamma value for the 2st chroma plane", > + OFFSET(gamma_r), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, 0.1, 10.0, > FLAGS }, gamma_{y,u,v} are probably better names. [...] -- FFmpeg = Furious Faithful Merciless Peaceless Elitarian Gadget _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel