Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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. static void create_lut(EQParameters *param)
  38. {
  39.     int i;
  40.     double   g  = 1.0 / param->gamma;
  41.     double   lw = 1.0 - param->gamma_weight;
  42.  
  43.     for (i = 0; i < 256; i++) {
  44.         double v = i / 255.0;
  45.         v = param->contrast * (v - 0.5) + 0.5 + param->brightness;
  46.  
  47.         if (v <= 0.0) {
  48.             param->lut[i] = 0;
  49.         } else {
  50.             v = v * lw + pow(v, g) * param->gamma_weight;
  51.  
  52.             if (v >= 1.0)
  53.                 param->lut[i] = 255;
  54.             else
  55.                 param->lut[i] = 256.0 * v;
  56.         }
  57.     }
  58.  
  59.     param->lut_clean = 1;
  60. }
  61.  
  62. 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.     if (!param->lut_clean)
  68.         create_lut(param);
  69.  
  70.     for (y = 0; y < h; y++) {
  71.         for (x = 0; x < w; x++) {
  72.             dst[y * dst_stride + x] = param->lut[src[y * src_stride + x]];
  73.         }
  74.     }
  75. }
  76.  
  77. 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.     int contrast = (int) (param->contrast * 256 * 16);
  83.     int brightness = ((int) (100.0 * param->brightness + 100.0) * 511) / 200 - 128 - contrast / 32;
  84.  
  85.     for (y = 0; y < h; y++) {
  86.         for (x = 0; x < w; x++) {
  87.             pel = ((src[y * src_stride + x] * contrast) >> 12) + brightness;
  88.  
  89.             if (pel & ~255)
  90.                 pel = (-pel) >> 31;
  91.  
  92.             dst[y * dst_stride + x] = pel;
  93.         }
  94.     }
  95. }
  96.  
  97. static void check_values(EQParameters *param, EQContext *eq)
  98. {
  99.     if (param->contrast == 1.0 && param->brightness == 0.0 && param->gamma == 1.0)
  100.         param->adjust = NULL;
  101.     else if (param->gamma == 1.0 && fabs(param->contrast) < 7.9)
  102.         param->adjust = eq->process;
  103.     else
  104.         param->adjust = apply_lut;
  105. }
  106.  
  107. static void set_contrast(EQContext *eq)
  108. {
  109.     eq->contrast = av_clipf(av_expr_eval(eq->contrast_pexpr, eq->var_values, eq), -1000.0, 1000.0);
  110.     eq->param[0].contrast = eq->contrast;
  111.     eq->param[0].lut_clean = 0;
  112.     check_values(&eq->param[0], eq);
  113. }
  114.  
  115. static void set_brightness(EQContext *eq)
  116. {
  117.     eq->brightness = av_clipf(av_expr_eval(eq->brightness_pexpr, eq->var_values, eq), -1.0, 1.0);
  118.     eq->param[0].brightness = eq->brightness;
  119.     eq->param[0].lut_clean = 0;
  120.     check_values(&eq->param[0], eq);
  121. }
  122.  
  123. static void set_gamma(EQContext *eq)
  124. {
  125.     int i;
  126.  
  127.     eq->gamma        = av_clipf(av_expr_eval(eq->gamma_pexpr,        eq->var_values, eq), 0.1, 10.0);
  128.     eq->gamma_r      = av_clipf(av_expr_eval(eq->gamma_r_pexpr,      eq->var_values, eq), 0.1, 10.0);
  129.     eq->gamma_g      = av_clipf(av_expr_eval(eq->gamma_g_pexpr,      eq->var_values, eq), 0.1, 10.0);
  130.     eq->gamma_b      = av_clipf(av_expr_eval(eq->gamma_b_pexpr,      eq->var_values, eq), 0.1, 10.0);
  131.     eq->gamma_weight = av_clipf(av_expr_eval(eq->gamma_weight_pexpr, eq->var_values, eq), 0.0,  1.0);
  132.  
  133.     eq->param[0].gamma = eq->gamma * eq->gamma_g;
  134.     eq->param[1].gamma = sqrt(eq->gamma_b / eq->gamma_g);
  135.     eq->param[2].gamma = sqrt(eq->gamma_r / eq->gamma_g);
  136.  
  137.     for (i = 0; i < 3; i++) {
  138.         eq->param[i].gamma_weight = eq->gamma_weight;
  139.         eq->param[i].lut_clean = 0;
  140.         check_values(&eq->param[i], eq);
  141.     }
  142. }
  143.  
  144. static void set_saturation(EQContext *eq)
  145. {
  146.     int i;
  147.  
  148.     eq->saturation = av_clipf(av_expr_eval(eq->saturation_pexpr, eq->var_values, eq), 0.0, 3.0);
  149.  
  150.     for (i = 1; i < 3; i++) {
  151.         eq->param[i].contrast = eq->saturation;
  152.         eq->param[i].lut_clean = 0;
  153.         check_values(&eq->param[i], eq);
  154.     }
  155. }
  156.  
  157. static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
  158. {
  159.     int ret;
  160.     AVExpr *old = NULL;
  161.  
  162.     if (*pexpr)
  163.         old = *pexpr;
  164.     ret = av_expr_parse(pexpr, expr, var_names, NULL, NULL, NULL, NULL, 0, log_ctx);
  165.     if (ret < 0) {
  166.         av_log(log_ctx, AV_LOG_ERROR,
  167.                "Error when parsing the expression '%s' for %s\n",
  168.                expr, option);
  169.         *pexpr = old;
  170.         return ret;
  171.     }
  172.  
  173.     av_expr_free(old);
  174.     return 0;
  175. }
  176.  
  177. static int initialize(AVFilterContext *ctx)
  178. {
  179.     EQContext *eq = ctx->priv;
  180.     int ret;
  181.  
  182.     eq->process = process_c;
  183.  
  184.     if ((ret = set_expr(&eq->contrast_pexpr,     eq->contrast_expr,     "contrast",     ctx)) < 0 ||
  185.         (ret = set_expr(&eq->brightness_pexpr,   eq->brightness_expr,   "brightness",   ctx)) < 0 ||
  186.         (ret = set_expr(&eq->saturation_pexpr,   eq->saturation_expr,   "saturation",   ctx)) < 0 ||
  187.         (ret = set_expr(&eq->gamma_pexpr,        eq->gamma_expr,        "gamma",        ctx)) < 0 ||
  188.         (ret = set_expr(&eq->gamma_r_pexpr,      eq->gamma_r_expr,      "gamma_r",      ctx)) < 0 ||
  189.         (ret = set_expr(&eq->gamma_g_pexpr,      eq->gamma_g_expr,      "gamma_g",      ctx)) < 0 ||
  190.         (ret = set_expr(&eq->gamma_b_pexpr,      eq->gamma_b_expr,      "gamma_b",      ctx)) < 0 ||
  191.         (ret = set_expr(&eq->gamma_weight_pexpr, eq->gamma_weight_expr, "gamma_weight", ctx)) < 0 )
  192.         return ret;
  193.  
  194.     if (ARCH_X86)
  195.         ff_eq_init_x86(eq);
  196.  
  197.     if (eq->eval_mode == EVAL_MODE_INIT) {
  198.         set_gamma(eq);
  199.         set_contrast(eq);
  200.         set_brightness(eq);
  201.         set_saturation(eq);
  202.     }
  203.  
  204.     return 0;
  205. }
  206.  
  207. static void uninit(AVFilterContext *ctx)
  208. {
  209.     EQContext *eq = ctx->priv;
  210.  
  211.     av_expr_free(eq->contrast_pexpr);     eq->contrast_pexpr     = NULL;
  212.     av_expr_free(eq->brightness_pexpr);   eq->brightness_pexpr   = NULL;
  213.     av_expr_free(eq->saturation_pexpr);   eq->saturation_pexpr   = NULL;
  214.     av_expr_free(eq->gamma_pexpr);        eq->gamma_pexpr        = NULL;
  215.     av_expr_free(eq->gamma_weight_pexpr); eq->gamma_weight_pexpr = NULL;
  216.     av_expr_free(eq->gamma_r_pexpr);      eq->gamma_r_pexpr      = NULL;
  217.     av_expr_free(eq->gamma_g_pexpr);      eq->gamma_g_pexpr      = NULL;
  218.     av_expr_free(eq->gamma_b_pexpr);      eq->gamma_b_pexpr      = NULL;
  219. }
  220.  
  221. static int config_props(AVFilterLink *inlink)
  222. {
  223.     EQContext *eq = inlink->dst->priv;
  224.  
  225.     eq->var_values[VAR_N] = 0;
  226.     eq->var_values[VAR_R] = inlink->frame_rate.num == 0 || inlink->frame_rate.den == 0 ?
  227.         NAN : av_q2d(inlink->frame_rate);
  228.  
  229.     return 0;
  230. }
  231.  
  232. 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.     AVFilterFormats *fmts_list = ff_make_format_list(pixel_fmts_eq);
  244.     if (!fmts_list)
  245.         return AVERROR(ENOMEM);
  246.     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. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  252. {
  253.     AVFilterContext *ctx = inlink->dst;
  254.     AVFilterLink *outlink = inlink->dst->outputs[0];
  255.     EQContext *eq = ctx->priv;
  256.     AVFrame *out;
  257.     int64_t pos = av_frame_get_pkt_pos(in);
  258.     const AVPixFmtDescriptor *desc;
  259.     int i;
  260.  
  261.     out = ff_get_video_buffer(outlink, inlink->w, inlink->h);
  262.     if (!out)
  263.         return AVERROR(ENOMEM);
  264.  
  265.     av_frame_copy_props(out, in);
  266.     desc = av_pix_fmt_desc_get(inlink->format);
  267.  
  268.     eq->var_values[VAR_N]   = inlink->frame_count;
  269.     eq->var_values[VAR_POS] = pos == -1 ? NAN : pos;
  270.     eq->var_values[VAR_T]   = TS2T(in->pts, inlink->time_base);
  271.  
  272.     if (eq->eval_mode == EVAL_MODE_FRAME) {
  273.         set_gamma(eq);
  274.         set_contrast(eq);
  275.         set_brightness(eq);
  276.         set_saturation(eq);
  277.     }
  278.  
  279.     for (i = 0; i < desc->nb_components; i++) {
  280.         int w = inlink->w;
  281.         int h = inlink->h;
  282.  
  283.         if (i == 1 || i == 2) {
  284.             w = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
  285.             h = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
  286.         }
  287.  
  288.         if (eq->param[i].adjust)
  289.             eq->param[i].adjust(&eq->param[i], out->data[i], out->linesize[i],
  290.                                  in->data[i], in->linesize[i], w, h);
  291.         else
  292.             av_image_copy_plane(out->data[i], out->linesize[i],
  293.                                 in->data[i], in->linesize[i], w, h);
  294.     }
  295.  
  296.     av_frame_free(&in);
  297.     return ff_filter_frame(outlink, out);
  298. }
  299.  
  300. static inline int set_param(AVExpr **pexpr, const char *args, const char *cmd,
  301.                             void (*set_fn)(EQContext *eq), AVFilterContext *ctx)
  302. {
  303.     EQContext *eq = ctx->priv;
  304.     int ret;
  305.     if ((ret = set_expr(pexpr, args, cmd, ctx)) < 0)
  306.         return ret;
  307.     if (eq->eval_mode == EVAL_MODE_INIT)
  308.         set_fn(eq);
  309.     return 0;
  310. }
  311.  
  312. static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
  313.                            char *res, int res_len, int flags)
  314. {
  315.     EQContext *eq = ctx->priv;
  316.  
  317. #define SET_PARAM(param_name, set_fn_name)                              \
  318.     if (!strcmp(cmd, #param_name)) return set_param(&eq->param_name##_pexpr, args, cmd, set_##set_fn_name, ctx);
  319.  
  320.          SET_PARAM(contrast, contrast)
  321.     else SET_PARAM(brightness, brightness)
  322.     else SET_PARAM(saturation, saturation)
  323.     else SET_PARAM(gamma, gamma)
  324.     else SET_PARAM(gamma_r, gamma)
  325.     else SET_PARAM(gamma_g, gamma)
  326.     else SET_PARAM(gamma_b, gamma)
  327.     else SET_PARAM(gamma_weight, gamma)
  328.     else return AVERROR(ENOSYS);
  329. }
  330.  
  331. static const AVFilterPad eq_inputs[] = {
  332.     {
  333.         .name = "default",
  334.         .type = AVMEDIA_TYPE_VIDEO,
  335.         .filter_frame = filter_frame,
  336.         .config_props = config_props,
  337.     },
  338.     { NULL }
  339. };
  340.  
  341. static const AVFilterPad eq_outputs[] = {
  342.     {
  343.         .name = "default",
  344.         .type = AVMEDIA_TYPE_VIDEO,
  345.     },
  346.     { NULL }
  347. };
  348.  
  349. #define OFFSET(x) offsetof(EQContext, x)
  350. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  351.  
  352. static const AVOption eq_options[] = {
  353.     { "contrast",     "set the contrast adjustment, negative values give a negative image",
  354.         OFFSET(contrast_expr),     AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  355.     { "brightness",   "set the brightness adjustment",
  356.         OFFSET(brightness_expr),   AV_OPT_TYPE_STRING, {.str = "0.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  357.     { "saturation",   "set the saturation adjustment",
  358.         OFFSET(saturation_expr),   AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  359.     { "gamma",        "set the initial gamma value",
  360.         OFFSET(gamma_expr),        AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  361.     { "gamma_r",      "gamma value for red",
  362.         OFFSET(gamma_r_expr),      AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  363.     { "gamma_g",      "gamma value for green",
  364.         OFFSET(gamma_g_expr),      AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  365.     { "gamma_b",      "gamma value for blue",
  366.         OFFSET(gamma_b_expr),      AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  367.     { "gamma_weight", "set the gamma weight which reduces the effect of gamma on bright areas",
  368.         OFFSET(gamma_weight_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS },
  369.     { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
  370.          { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
  371.          { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
  372.     { NULL }
  373. };
  374.  
  375. AVFILTER_DEFINE_CLASS(eq);
  376.  
  377. AVFilter ff_vf_eq = {
  378.     .name            = "eq",
  379.     .description     = NULL_IF_CONFIG_SMALL("Adjust brightness, contrast, gamma, and saturation."),
  380.     .priv_size       = sizeof(EQContext),
  381.     .priv_class      = &eq_class,
  382.     .inputs          = eq_inputs,
  383.     .outputs         = eq_outputs,
  384.     .process_command = process_command,
  385.     .query_formats   = query_formats,
  386.     .init            = initialize,
  387.     .uninit          = uninit,
  388. };
  389.