LCOV - code coverage report
Current view: top level - libavfilter - vf_geq.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 120 0.0 %
Date: 2017-12-12 11:08:38 Functions: 0 10 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
       3             :  * Copyright (C) 2012 Clément Bœsch <u pkh me>
       4             :  *
       5             :  * This file is part of FFmpeg.
       6             :  *
       7             :  * FFmpeg is free software; you can redistribute it and/or modify
       8             :  * it under the terms of the GNU General Public License as published by
       9             :  * the Free Software Foundation; either version 2 of the License, or
      10             :  * (at your option) any later version.
      11             :  *
      12             :  * FFmpeg is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :  * GNU General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU General Public License along
      18             :  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
      19             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20             :  */
      21             : 
      22             : /**
      23             :  * @file
      24             :  * Generic equation change filter
      25             :  * Originally written by Michael Niedermayer for the MPlayer project, and
      26             :  * ported by Clément Bœsch for FFmpeg.
      27             :  */
      28             : 
      29             : #include "libavutil/avassert.h"
      30             : #include "libavutil/avstring.h"
      31             : #include "libavutil/eval.h"
      32             : #include "libavutil/opt.h"
      33             : #include "libavutil/pixdesc.h"
      34             : #include "internal.h"
      35             : 
      36             : typedef struct GEQContext {
      37             :     const AVClass *class;
      38             :     AVExpr *e[4];               ///< expressions for each plane
      39             :     char *expr_str[4+3];        ///< expression strings for each plane
      40             :     AVFrame *picref;            ///< current input buffer
      41             :     int hsub, vsub;             ///< chroma subsampling
      42             :     int planes;                 ///< number of planes
      43             :     int is_rgb;
      44             :     int bps;
      45             : } GEQContext;
      46             : 
      47             : enum { Y = 0, U, V, A, G, B, R };
      48             : 
      49             : #define OFFSET(x) offsetof(GEQContext, x)
      50             : #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      51             : 
      52             : static const AVOption geq_options[] = {
      53             :     { "lum_expr",   "set luminance expression",   OFFSET(expr_str[Y]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      54             :     { "lum",        "set luminance expression",   OFFSET(expr_str[Y]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      55             :     { "cb_expr",    "set chroma blue expression", OFFSET(expr_str[U]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      56             :     { "cb",         "set chroma blue expression", OFFSET(expr_str[U]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      57             :     { "cr_expr",    "set chroma red expression",  OFFSET(expr_str[V]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      58             :     { "cr",         "set chroma red expression",  OFFSET(expr_str[V]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      59             :     { "alpha_expr", "set alpha expression",       OFFSET(expr_str[A]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      60             :     { "a",          "set alpha expression",       OFFSET(expr_str[A]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      61             :     { "red_expr",   "set red expression",         OFFSET(expr_str[R]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      62             :     { "r",          "set red expression",         OFFSET(expr_str[R]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      63             :     { "green_expr", "set green expression",       OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      64             :     { "g",          "set green expression",       OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      65             :     { "blue_expr",  "set blue expression",        OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      66             :     { "b",          "set blue expression",        OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
      67             :     {NULL},
      68             : };
      69             : 
      70             : AVFILTER_DEFINE_CLASS(geq);
      71             : 
      72           0 : static inline double getpix(void *priv, double x, double y, int plane)
      73             : {
      74             :     int xi, yi;
      75           0 :     GEQContext *geq = priv;
      76           0 :     AVFrame *picref = geq->picref;
      77           0 :     const uint8_t *src = picref->data[plane];
      78           0 :     int linesize = picref->linesize[plane];
      79           0 :     const int w = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->width,  geq->hsub) : picref->width;
      80           0 :     const int h = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->height, geq->vsub) : picref->height;
      81             : 
      82           0 :     if (!src)
      83           0 :         return 0;
      84             : 
      85           0 :     xi = x = av_clipf(x, 0, w - 2);
      86           0 :     yi = y = av_clipf(y, 0, h - 2);
      87             : 
      88           0 :     x -= xi;
      89           0 :     y -= yi;
      90             : 
      91           0 :     if (geq->bps > 8) {
      92           0 :         const uint16_t *src16 = (const uint16_t*)src;
      93           0 :         linesize /= 2;
      94             : 
      95           0 :         return (1-y)*((1-x)*src16[xi +  yi    * linesize] + x*src16[xi + 1 +  yi    * linesize])
      96           0 :               +   y *((1-x)*src16[xi + (yi+1) * linesize] + x*src16[xi + 1 + (yi+1) * linesize]);
      97             :     } else {
      98           0 :         return (1-y)*((1-x)*src[xi +  yi    * linesize] + x*src[xi + 1 +  yi    * linesize])
      99           0 :               +   y *((1-x)*src[xi + (yi+1) * linesize] + x*src[xi + 1 + (yi+1) * linesize]);
     100             :     }
     101             : }
     102             : 
     103             : //TODO: cubic interpolate
     104             : //TODO: keep the last few frames
     105           0 : static double lum(void *priv, double x, double y) { return getpix(priv, x, y, 0); }
     106           0 : static double  cb(void *priv, double x, double y) { return getpix(priv, x, y, 1); }
     107           0 : static double  cr(void *priv, double x, double y) { return getpix(priv, x, y, 2); }
     108           0 : static double alpha(void *priv, double x, double y) { return getpix(priv, x, y, 3); }
     109             : 
     110             : static const char *const var_names[] = {   "X",   "Y",   "W",   "H",   "N",   "SW",   "SH",   "T",        NULL };
     111             : enum                                   { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_N, VAR_SW, VAR_SH, VAR_T, VAR_VARS_NB };
     112             : 
     113           0 : static av_cold int geq_init(AVFilterContext *ctx)
     114             : {
     115           0 :     GEQContext *geq = ctx->priv;
     116           0 :     int plane, ret = 0;
     117             : 
     118           0 :     if (!geq->expr_str[Y] && !geq->expr_str[G] && !geq->expr_str[B] && !geq->expr_str[R]) {
     119           0 :         av_log(ctx, AV_LOG_ERROR, "A luminance or RGB expression is mandatory\n");
     120           0 :         ret = AVERROR(EINVAL);
     121           0 :         goto end;
     122             :     }
     123           0 :     geq->is_rgb = !geq->expr_str[Y];
     124             : 
     125           0 :     if ((geq->expr_str[Y] || geq->expr_str[U] || geq->expr_str[V]) && (geq->expr_str[G] || geq->expr_str[B] || geq->expr_str[R])) {
     126           0 :         av_log(ctx, AV_LOG_ERROR, "Either YCbCr or RGB but not both must be specified\n");
     127           0 :         ret = AVERROR(EINVAL);
     128           0 :         goto end;
     129             :     }
     130             : 
     131           0 :     if (!geq->expr_str[U] && !geq->expr_str[V]) {
     132             :         /* No chroma at all: fallback on luma */
     133           0 :         geq->expr_str[U] = av_strdup(geq->expr_str[Y]);
     134           0 :         geq->expr_str[V] = av_strdup(geq->expr_str[Y]);
     135             :     } else {
     136             :         /* One chroma unspecified, fallback on the other */
     137           0 :         if (!geq->expr_str[U]) geq->expr_str[U] = av_strdup(geq->expr_str[V]);
     138           0 :         if (!geq->expr_str[V]) geq->expr_str[V] = av_strdup(geq->expr_str[U]);
     139             :     }
     140             : 
     141           0 :     if (!geq->expr_str[A]) {
     142             :         char bps_string[8];
     143           0 :         snprintf(bps_string, sizeof(bps_string), "%d", (1<<geq->bps) - 1);
     144           0 :         geq->expr_str[A] = av_strdup(bps_string);
     145             :     }
     146           0 :     if (!geq->expr_str[G])
     147           0 :         geq->expr_str[G] = av_strdup("g(X,Y)");
     148           0 :     if (!geq->expr_str[B])
     149           0 :         geq->expr_str[B] = av_strdup("b(X,Y)");
     150           0 :     if (!geq->expr_str[R])
     151           0 :         geq->expr_str[R] = av_strdup("r(X,Y)");
     152             : 
     153           0 :     if (geq->is_rgb ?
     154           0 :             (!geq->expr_str[G] || !geq->expr_str[B] || !geq->expr_str[R])
     155             :                     :
     156           0 :             (!geq->expr_str[U] || !geq->expr_str[V] || !geq->expr_str[A])) {
     157           0 :         ret = AVERROR(ENOMEM);
     158           0 :         goto end;
     159             :     }
     160             : 
     161           0 :     for (plane = 0; plane < 4; plane++) {
     162             :         static double (*p[])(void *, double, double) = { lum, cb, cr, alpha };
     163             :         static const char *const func2_yuv_names[]    = { "lum", "cb", "cr", "alpha", "p", NULL };
     164             :         static const char *const func2_rgb_names[]    = { "g", "b", "r", "alpha", "p", NULL };
     165           0 :         const char *const *func2_names       = geq->is_rgb ? func2_rgb_names : func2_yuv_names;
     166           0 :         double (*func2[])(void *, double, double) = { lum, cb, cr, alpha, p[plane], NULL };
     167             : 
     168           0 :         ret = av_expr_parse(&geq->e[plane], geq->expr_str[plane < 3 && geq->is_rgb ? plane+4 : plane], var_names,
     169             :                             NULL, NULL, func2_names, func2, 0, ctx);
     170           0 :         if (ret < 0)
     171           0 :             break;
     172             :     }
     173             : 
     174           0 : end:
     175           0 :     return ret;
     176             : }
     177             : 
     178           0 : static int geq_query_formats(AVFilterContext *ctx)
     179             : {
     180           0 :     GEQContext *geq = ctx->priv;
     181             :     static const enum AVPixelFormat yuv_pix_fmts[] = {
     182             :         AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
     183             :         AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,  AV_PIX_FMT_YUV440P,
     184             :         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
     185             :         AV_PIX_FMT_GRAY8,
     186             :         AV_PIX_FMT_YUV444P9,  AV_PIX_FMT_YUV422P9,  AV_PIX_FMT_YUV420P9,
     187             :         AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA420P9,
     188             :         AV_PIX_FMT_YUV444P10,  AV_PIX_FMT_YUV422P10,  AV_PIX_FMT_YUV420P10,
     189             :         AV_PIX_FMT_YUV440P10,
     190             :         AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10,
     191             :         AV_PIX_FMT_GRAY10,
     192             :         AV_PIX_FMT_YUV444P12,  AV_PIX_FMT_YUV422P12,  AV_PIX_FMT_YUV420P12,
     193             :         AV_PIX_FMT_GRAY12,
     194             :         AV_PIX_FMT_YUV444P14,  AV_PIX_FMT_YUV422P14,  AV_PIX_FMT_YUV420P14,
     195             :         AV_PIX_FMT_YUV444P16,  AV_PIX_FMT_YUV422P16,  AV_PIX_FMT_YUV420P16,
     196             :         AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA420P16,
     197             :         AV_PIX_FMT_GRAY16,
     198             :         AV_PIX_FMT_NONE
     199             :     };
     200             :     static const enum AVPixelFormat rgb_pix_fmts[] = {
     201             :         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
     202             :         AV_PIX_FMT_GBRP9,
     203             :         AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
     204             :         AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
     205             :         AV_PIX_FMT_GBRP14,
     206             :         AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
     207             :         AV_PIX_FMT_NONE
     208             :     };
     209             :     AVFilterFormats *fmts_list;
     210             : 
     211           0 :     if (geq->is_rgb) {
     212           0 :         fmts_list = ff_make_format_list(rgb_pix_fmts);
     213             :     } else
     214           0 :         fmts_list = ff_make_format_list(yuv_pix_fmts);
     215           0 :     if (!fmts_list)
     216           0 :         return AVERROR(ENOMEM);
     217           0 :     return ff_set_common_formats(ctx, fmts_list);
     218             : }
     219             : 
     220           0 : static int geq_config_props(AVFilterLink *inlink)
     221             : {
     222           0 :     GEQContext *geq = inlink->dst->priv;
     223           0 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     224             : 
     225           0 :     av_assert0(desc);
     226             : 
     227           0 :     geq->hsub = desc->log2_chroma_w;
     228           0 :     geq->vsub = desc->log2_chroma_h;
     229           0 :     geq->planes = desc->nb_components;
     230           0 :     geq->bps    = desc->comp[0].depth;
     231             : 
     232           0 :     return 0;
     233             : }
     234             : 
     235           0 : static int geq_filter_frame(AVFilterLink *inlink, AVFrame *in)
     236             : {
     237             :     int plane;
     238           0 :     GEQContext *geq = inlink->dst->priv;
     239           0 :     AVFilterLink *outlink = inlink->dst->outputs[0];
     240             :     AVFrame *out;
     241           0 :     double values[VAR_VARS_NB] = {
     242           0 :         [VAR_N] = inlink->frame_count_out,
     243           0 :         [VAR_T] = in->pts == AV_NOPTS_VALUE ? NAN : in->pts * av_q2d(inlink->time_base),
     244             :     };
     245             : 
     246           0 :     geq->picref = in;
     247           0 :     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     248           0 :     if (!out) {
     249           0 :         av_frame_free(&in);
     250           0 :         return AVERROR(ENOMEM);
     251             :     }
     252           0 :     av_frame_copy_props(out, in);
     253             : 
     254           0 :     for (plane = 0; plane < geq->planes && out->data[plane]; plane++) {
     255             :         int x, y;
     256           0 :         uint8_t *dst = out->data[plane];
     257           0 :         uint16_t *dst16 = (uint16_t*)out->data[plane];
     258           0 :         const int linesize = out->linesize[plane];
     259           0 :         const int w = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(inlink->w, geq->hsub) : inlink->w;
     260           0 :         const int h = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(inlink->h, geq->vsub) : inlink->h;
     261             : 
     262           0 :         values[VAR_W]  = w;
     263           0 :         values[VAR_H]  = h;
     264           0 :         values[VAR_SW] = w / (double)inlink->w;
     265           0 :         values[VAR_SH] = h / (double)inlink->h;
     266             : 
     267           0 :         for (y = 0; y < h; y++) {
     268           0 :             values[VAR_Y] = y;
     269           0 :             if (geq->bps > 8) {
     270           0 :                 for (x = 0; x < w; x++) {
     271           0 :                     values[VAR_X] = x;
     272           0 :                     dst16[x] = av_expr_eval(geq->e[plane], values, geq);
     273             :                 }
     274           0 :                 dst16 += linesize / 2;
     275             :             } else {
     276           0 :                 for (x = 0; x < w; x++) {
     277           0 :                     values[VAR_X] = x;
     278           0 :                     dst[x] = av_expr_eval(geq->e[plane], values, geq);
     279             :                 }
     280           0 :                 dst += linesize;
     281             :             }
     282             :         }
     283             :     }
     284             : 
     285           0 :     av_frame_free(&geq->picref);
     286           0 :     return ff_filter_frame(outlink, out);
     287             : }
     288             : 
     289           0 : static av_cold void geq_uninit(AVFilterContext *ctx)
     290             : {
     291             :     int i;
     292           0 :     GEQContext *geq = ctx->priv;
     293             : 
     294           0 :     for (i = 0; i < FF_ARRAY_ELEMS(geq->e); i++)
     295           0 :         av_expr_free(geq->e[i]);
     296           0 : }
     297             : 
     298             : static const AVFilterPad geq_inputs[] = {
     299             :     {
     300             :         .name         = "default",
     301             :         .type         = AVMEDIA_TYPE_VIDEO,
     302             :         .config_props = geq_config_props,
     303             :         .filter_frame = geq_filter_frame,
     304             :     },
     305             :     { NULL }
     306             : };
     307             : 
     308             : static const AVFilterPad geq_outputs[] = {
     309             :     {
     310             :         .name = "default",
     311             :         .type = AVMEDIA_TYPE_VIDEO,
     312             :     },
     313             :     { NULL }
     314             : };
     315             : 
     316             : AVFilter ff_vf_geq = {
     317             :     .name          = "geq",
     318             :     .description   = NULL_IF_CONFIG_SMALL("Apply generic equation to each pixel."),
     319             :     .priv_size     = sizeof(GEQContext),
     320             :     .init          = geq_init,
     321             :     .uninit        = geq_uninit,
     322             :     .query_formats = geq_query_formats,
     323             :     .inputs        = geq_inputs,
     324             :     .outputs       = geq_outputs,
     325             :     .priv_class    = &geq_class,
     326             :     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
     327             : };

Generated by: LCOV version 1.13