LCOV - code coverage report
Current view: top level - libavfilter - vf_eq.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 173 0.0 %
Date: 2017-12-10 21:22:29 Functions: 0 16 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Original MPlayer filters by Richard Felker, Hampa Hug, Daniel Moreno,
       3             :  * and Michael Niedermeyer.
       4             :  *
       5             :  * Copyright (c) 2014 James Darnley <james.darnley@gmail.com>
       6             :  * Copyright (c) 2015 Arwa Arif <arwaarif1994@gmail.com>
       7             :  *
       8             :  * This file is part of FFmpeg.
       9             :  *
      10             :  * FFmpeg is free software; you can redistribute it and/or modify
      11             :  * it under the terms of the GNU General Public License as published by
      12             :  * the Free Software Foundation; either version 2 of the License, or
      13             :  * (at your option) any later version.
      14             :  *
      15             :  * FFmpeg is distributed in the hope that it will be useful,
      16             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  * GNU General Public License for more details.
      19             :  *
      20             :  * You should have received a copy of the GNU General Public License along
      21             :  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
      22             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      23             :  */
      24             : 
      25             : /**
      26             :  * @file
      27             :  * very simple video equalizer
      28             :  */
      29             : 
      30             : #include "libavfilter/internal.h"
      31             : #include "libavutil/common.h"
      32             : #include "libavutil/imgutils.h"
      33             : #include "libavutil/opt.h"
      34             : #include "libavutil/pixdesc.h"
      35             : #include "vf_eq.h"
      36             : 
      37           0 : static void create_lut(EQParameters *param)
      38             : {
      39             :     int i;
      40           0 :     double   g  = 1.0 / param->gamma;
      41           0 :     double   lw = 1.0 - param->gamma_weight;
      42             : 
      43           0 :     for (i = 0; i < 256; i++) {
      44           0 :         double v = i / 255.0;
      45           0 :         v = param->contrast * (v - 0.5) + 0.5 + param->brightness;
      46             : 
      47           0 :         if (v <= 0.0) {
      48           0 :             param->lut[i] = 0;
      49             :         } else {
      50           0 :             v = v * lw + pow(v, g) * param->gamma_weight;
      51             : 
      52           0 :             if (v >= 1.0)
      53           0 :                 param->lut[i] = 255;
      54             :             else
      55           0 :                 param->lut[i] = 256.0 * v;
      56             :         }
      57             :     }
      58             : 
      59           0 :     param->lut_clean = 1;
      60           0 : }
      61             : 
      62           0 : static void apply_lut(EQParameters *param, uint8_t *dst, int dst_stride,
      63             :                       const uint8_t *src, int src_stride, int w, int h)
      64             : {
      65             :     int x, y;
      66             : 
      67           0 :     if (!param->lut_clean)
      68           0 :         create_lut(param);
      69             : 
      70           0 :     for (y = 0; y < h; y++) {
      71           0 :         for (x = 0; x < w; x++) {
      72           0 :             dst[y * dst_stride + x] = param->lut[src[y * src_stride + x]];
      73             :         }
      74             :     }
      75           0 : }
      76             : 
      77           0 : static void process_c(EQParameters *param, uint8_t *dst, int dst_stride,
      78             :                       const uint8_t *src, int src_stride, int w, int h)
      79             : {
      80             :     int x, y, pel;
      81             : 
      82           0 :     int contrast = (int) (param->contrast * 256 * 16);
      83           0 :     int brightness = ((int) (100.0 * param->brightness + 100.0) * 511) / 200 - 128 - contrast / 32;
      84             : 
      85           0 :     for (y = 0; y < h; y++) {
      86           0 :         for (x = 0; x < w; x++) {
      87           0 :             pel = ((src[y * src_stride + x] * contrast) >> 12) + brightness;
      88             : 
      89           0 :             if (pel & ~255)
      90           0 :                 pel = (-pel) >> 31;
      91             : 
      92           0 :             dst[y * dst_stride + x] = pel;
      93             :         }
      94             :     }
      95           0 : }
      96             : 
      97           0 : static void check_values(EQParameters *param, EQContext *eq)
      98             : {
      99           0 :     if (param->contrast == 1.0 && param->brightness == 0.0 && param->gamma == 1.0)
     100           0 :         param->adjust = NULL;
     101           0 :     else if (param->gamma == 1.0 && fabs(param->contrast) < 7.9)
     102           0 :         param->adjust = eq->process;
     103             :     else
     104           0 :         param->adjust = apply_lut;
     105           0 : }
     106             : 
     107           0 : static void set_contrast(EQContext *eq)
     108             : {
     109           0 :     eq->contrast = av_clipf(av_expr_eval(eq->contrast_pexpr, eq->var_values, eq), -1000.0, 1000.0);
     110           0 :     eq->param[0].contrast = eq->contrast;
     111           0 :     eq->param[0].lut_clean = 0;
     112           0 :     check_values(&eq->param[0], eq);
     113           0 : }
     114             : 
     115           0 : static void set_brightness(EQContext *eq)
     116             : {
     117           0 :     eq->brightness = av_clipf(av_expr_eval(eq->brightness_pexpr, eq->var_values, eq), -1.0, 1.0);
     118           0 :     eq->param[0].brightness = eq->brightness;
     119           0 :     eq->param[0].lut_clean = 0;
     120           0 :     check_values(&eq->param[0], eq);
     121           0 : }
     122             : 
     123           0 : static void set_gamma(EQContext *eq)
     124             : {
     125             :     int i;
     126             : 
     127           0 :     eq->gamma        = av_clipf(av_expr_eval(eq->gamma_pexpr,        eq->var_values, eq), 0.1, 10.0);
     128           0 :     eq->gamma_r      = av_clipf(av_expr_eval(eq->gamma_r_pexpr,      eq->var_values, eq), 0.1, 10.0);
     129           0 :     eq->gamma_g      = av_clipf(av_expr_eval(eq->gamma_g_pexpr,      eq->var_values, eq), 0.1, 10.0);
     130           0 :     eq->gamma_b      = av_clipf(av_expr_eval(eq->gamma_b_pexpr,      eq->var_values, eq), 0.1, 10.0);
     131           0 :     eq->gamma_weight = av_clipf(av_expr_eval(eq->gamma_weight_pexpr, eq->var_values, eq), 0.0,  1.0);
     132             : 
     133           0 :     eq->param[0].gamma = eq->gamma * eq->gamma_g;
     134           0 :     eq->param[1].gamma = sqrt(eq->gamma_b / eq->gamma_g);
     135           0 :     eq->param[2].gamma = sqrt(eq->gamma_r / eq->gamma_g);
     136             : 
     137           0 :     for (i = 0; i < 3; i++) {
     138           0 :         eq->param[i].gamma_weight = eq->gamma_weight;
     139           0 :         eq->param[i].lut_clean = 0;
     140           0 :         check_values(&eq->param[i], eq);
     141             :     }
     142           0 : }
     143             : 
     144           0 : static void set_saturation(EQContext *eq)
     145             : {
     146             :     int i;
     147             : 
     148           0 :     eq->saturation = av_clipf(av_expr_eval(eq->saturation_pexpr, eq->var_values, eq), 0.0, 3.0);
     149             : 
     150           0 :     for (i = 1; i < 3; i++) {
     151           0 :         eq->param[i].contrast = eq->saturation;
     152           0 :         eq->param[i].lut_clean = 0;
     153           0 :         check_values(&eq->param[i], eq);
     154             :     }
     155           0 : }
     156             : 
     157           0 : static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
     158             : {
     159             :     int ret;
     160           0 :     AVExpr *old = NULL;
     161             : 
     162           0 :     if (*pexpr)
     163           0 :         old = *pexpr;
     164           0 :     ret = av_expr_parse(pexpr, expr, var_names, NULL, NULL, NULL, NULL, 0, log_ctx);
     165           0 :     if (ret < 0) {
     166           0 :         av_log(log_ctx, AV_LOG_ERROR,
     167             :                "Error when parsing the expression '%s' for %s\n",
     168             :                expr, option);
     169           0 :         *pexpr = old;
     170           0 :         return ret;
     171             :     }
     172             : 
     173           0 :     av_expr_free(old);
     174           0 :     return 0;
     175             : }
     176             : 
     177           0 : static int initialize(AVFilterContext *ctx)
     178             : {
     179           0 :     EQContext *eq = ctx->priv;
     180             :     int ret;
     181             : 
     182           0 :     eq->process = process_c;
     183             : 
     184           0 :     if ((ret = set_expr(&eq->contrast_pexpr,     eq->contrast_expr,     "contrast",     ctx)) < 0 ||
     185           0 :         (ret = set_expr(&eq->brightness_pexpr,   eq->brightness_expr,   "brightness",   ctx)) < 0 ||
     186           0 :         (ret = set_expr(&eq->saturation_pexpr,   eq->saturation_expr,   "saturation",   ctx)) < 0 ||
     187           0 :         (ret = set_expr(&eq->gamma_pexpr,        eq->gamma_expr,        "gamma",        ctx)) < 0 ||
     188           0 :         (ret = set_expr(&eq->gamma_r_pexpr,      eq->gamma_r_expr,      "gamma_r",      ctx)) < 0 ||
     189           0 :         (ret = set_expr(&eq->gamma_g_pexpr,      eq->gamma_g_expr,      "gamma_g",      ctx)) < 0 ||
     190           0 :         (ret = set_expr(&eq->gamma_b_pexpr,      eq->gamma_b_expr,      "gamma_b",      ctx)) < 0 ||
     191           0 :         (ret = set_expr(&eq->gamma_weight_pexpr, eq->gamma_weight_expr, "gamma_weight", ctx)) < 0 )
     192           0 :         return ret;
     193             : 
     194             :     if (ARCH_X86)
     195           0 :         ff_eq_init_x86(eq);
     196             : 
     197           0 :     if (eq->eval_mode == EVAL_MODE_INIT) {
     198           0 :         set_gamma(eq);
     199           0 :         set_contrast(eq);
     200           0 :         set_brightness(eq);
     201           0 :         set_saturation(eq);
     202             :     }
     203             : 
     204           0 :     return 0;
     205             : }
     206             : 
     207           0 : static void uninit(AVFilterContext *ctx)
     208             : {
     209           0 :     EQContext *eq = ctx->priv;
     210             : 
     211           0 :     av_expr_free(eq->contrast_pexpr);     eq->contrast_pexpr     = NULL;
     212           0 :     av_expr_free(eq->brightness_pexpr);   eq->brightness_pexpr   = NULL;
     213           0 :     av_expr_free(eq->saturation_pexpr);   eq->saturation_pexpr   = NULL;
     214           0 :     av_expr_free(eq->gamma_pexpr);        eq->gamma_pexpr        = NULL;
     215           0 :     av_expr_free(eq->gamma_weight_pexpr); eq->gamma_weight_pexpr = NULL;
     216           0 :     av_expr_free(eq->gamma_r_pexpr);      eq->gamma_r_pexpr      = NULL;
     217           0 :     av_expr_free(eq->gamma_g_pexpr);      eq->gamma_g_pexpr      = NULL;
     218           0 :     av_expr_free(eq->gamma_b_pexpr);      eq->gamma_b_pexpr      = NULL;
     219           0 : }
     220             : 
     221           0 : static int config_props(AVFilterLink *inlink)
     222             : {
     223           0 :     EQContext *eq = inlink->dst->priv;
     224             : 
     225           0 :     eq->var_values[VAR_N] = 0;
     226           0 :     eq->var_values[VAR_R] = inlink->frame_rate.num == 0 || inlink->frame_rate.den == 0 ?
     227           0 :         NAN : av_q2d(inlink->frame_rate);
     228             : 
     229           0 :     return 0;
     230             : }
     231             : 
     232           0 : static int query_formats(AVFilterContext *ctx)
     233             : {
     234             :     static const enum AVPixelFormat pixel_fmts_eq[] = {
     235             :         AV_PIX_FMT_GRAY8,
     236             :         AV_PIX_FMT_YUV410P,
     237             :         AV_PIX_FMT_YUV411P,
     238             :         AV_PIX_FMT_YUV420P,
     239             :         AV_PIX_FMT_YUV422P,
     240             :         AV_PIX_FMT_YUV444P,
     241             :         AV_PIX_FMT_NONE
     242             :     };
     243           0 :     AVFilterFormats *fmts_list = ff_make_format_list(pixel_fmts_eq);
     244           0 :     if (!fmts_list)
     245           0 :         return AVERROR(ENOMEM);
     246           0 :     return ff_set_common_formats(ctx, fmts_list);
     247             : }
     248             : 
     249             : #define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb))
     250             : 
     251           0 : static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     252             : {
     253           0 :     AVFilterContext *ctx = inlink->dst;
     254           0 :     AVFilterLink *outlink = inlink->dst->outputs[0];
     255           0 :     EQContext *eq = ctx->priv;
     256             :     AVFrame *out;
     257           0 :     int64_t pos = in->pkt_pos;
     258             :     const AVPixFmtDescriptor *desc;
     259             :     int i;
     260             : 
     261           0 :     out = ff_get_video_buffer(outlink, inlink->w, inlink->h);
     262           0 :     if (!out) {
     263           0 :         av_frame_free(&in);
     264           0 :         return AVERROR(ENOMEM);
     265             :     }
     266             : 
     267           0 :     av_frame_copy_props(out, in);
     268           0 :     desc = av_pix_fmt_desc_get(inlink->format);
     269             : 
     270           0 :     eq->var_values[VAR_N]   = inlink->frame_count_out;
     271           0 :     eq->var_values[VAR_POS] = pos == -1 ? NAN : pos;
     272           0 :     eq->var_values[VAR_T]   = TS2T(in->pts, inlink->time_base);
     273             : 
     274           0 :     if (eq->eval_mode == EVAL_MODE_FRAME) {
     275           0 :         set_gamma(eq);
     276           0 :         set_contrast(eq);
     277           0 :         set_brightness(eq);
     278           0 :         set_saturation(eq);
     279             :     }
     280             : 
     281           0 :     for (i = 0; i < desc->nb_components; i++) {
     282           0 :         int w = inlink->w;
     283           0 :         int h = inlink->h;
     284             : 
     285           0 :         if (i == 1 || i == 2) {
     286           0 :             w = AV_CEIL_RSHIFT(w, desc->log2_chroma_w);
     287           0 :             h = AV_CEIL_RSHIFT(h, desc->log2_chroma_h);
     288             :         }
     289             : 
     290           0 :         if (eq->param[i].adjust)
     291           0 :             eq->param[i].adjust(&eq->param[i], out->data[i], out->linesize[i],
     292           0 :                                  in->data[i], in->linesize[i], w, h);
     293             :         else
     294           0 :             av_image_copy_plane(out->data[i], out->linesize[i],
     295           0 :                                 in->data[i], in->linesize[i], w, h);
     296             :     }
     297             : 
     298           0 :     av_frame_free(&in);
     299           0 :     return ff_filter_frame(outlink, out);
     300             : }
     301             : 
     302           0 : static inline int set_param(AVExpr **pexpr, const char *args, const char *cmd,
     303             :                             void (*set_fn)(EQContext *eq), AVFilterContext *ctx)
     304             : {
     305           0 :     EQContext *eq = ctx->priv;
     306             :     int ret;
     307           0 :     if ((ret = set_expr(pexpr, args, cmd, ctx)) < 0)
     308           0 :         return ret;
     309           0 :     if (eq->eval_mode == EVAL_MODE_INIT)
     310           0 :         set_fn(eq);
     311           0 :     return 0;
     312             : }
     313             : 
     314           0 : static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
     315             :                            char *res, int res_len, int flags)
     316             : {
     317           0 :     EQContext *eq = ctx->priv;
     318             : 
     319             : #define SET_PARAM(param_name, set_fn_name)                              \
     320             :     if (!strcmp(cmd, #param_name)) return set_param(&eq->param_name##_pexpr, args, cmd, set_##set_fn_name, ctx);
     321             : 
     322           0 :          SET_PARAM(contrast, contrast)
     323           0 :     else SET_PARAM(brightness, brightness)
     324           0 :     else SET_PARAM(saturation, saturation)
     325           0 :     else SET_PARAM(gamma, gamma)
     326           0 :     else SET_PARAM(gamma_r, gamma)
     327           0 :     else SET_PARAM(gamma_g, gamma)
     328           0 :     else SET_PARAM(gamma_b, gamma)
     329           0 :     else SET_PARAM(gamma_weight, gamma)
     330           0 :     else return AVERROR(ENOSYS);
     331             : }
     332             : 
     333             : static const AVFilterPad eq_inputs[] = {
     334             :     {
     335             :         .name = "default",
     336             :         .type = AVMEDIA_TYPE_VIDEO,
     337             :         .filter_frame = filter_frame,
     338             :         .config_props = config_props,
     339             :     },
     340             :     { NULL }
     341             : };
     342             : 
     343             : static const AVFilterPad eq_outputs[] = {
     344             :     {
     345             :         .name = "default",
     346             :         .type = AVMEDIA_TYPE_VIDEO,
     347             :     },
     348             :     { NULL }
     349             : };
     350             : 
     351             : #define OFFSET(x) offsetof(EQContext, x)
     352             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
     353             : 
     354             : static const AVOption eq_options[] = {
     355             :     { "contrast",     "set the contrast adjustment, negative values give a negative image",
     356             :         OFFSET(contrast_expr),     AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     357             :     { "brightness",   "set the brightness adjustment",
     358             :         OFFSET(brightness_expr),   AV_OPT_TYPE_STRING, {.str = "0.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     359             :     { "saturation",   "set the saturation adjustment",
     360             :         OFFSET(saturation_expr),   AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     361             :     { "gamma",        "set the initial gamma value",
     362             :         OFFSET(gamma_expr),        AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     363             :     { "gamma_r",      "gamma value for red",
     364             :         OFFSET(gamma_r_expr),      AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     365             :     { "gamma_g",      "gamma value for green",
     366             :         OFFSET(gamma_g_expr),      AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     367             :     { "gamma_b",      "gamma value for blue",
     368             :         OFFSET(gamma_b_expr),      AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     369             :     { "gamma_weight", "set the gamma weight which reduces the effect of gamma on bright areas",
     370             :         OFFSET(gamma_weight_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
     371             :     { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
     372             :          { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
     373             :          { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
     374             :     { NULL }
     375             : };
     376             : 
     377             : AVFILTER_DEFINE_CLASS(eq);
     378             : 
     379             : AVFilter ff_vf_eq = {
     380             :     .name            = "eq",
     381             :     .description     = NULL_IF_CONFIG_SMALL("Adjust brightness, contrast, gamma, and saturation."),
     382             :     .priv_size       = sizeof(EQContext),
     383             :     .priv_class      = &eq_class,
     384             :     .inputs          = eq_inputs,
     385             :     .outputs         = eq_outputs,
     386             :     .process_command = process_command,
     387             :     .query_formats   = query_formats,
     388             :     .init            = initialize,
     389             :     .uninit          = uninit,
     390             :     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
     391             : };

Generated by: LCOV version 1.13