LCOV - code coverage report
Current view: top level - libavfilter - vf_crop.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 116 160 72.5 %
Date: 2017-12-15 18:13:28 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2007 Bobby Bingham
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       6             :  * FFmpeg is free software; you can redistribute it and/or
       7             :  * modify it under the terms of the GNU Lesser General Public
       8             :  * License as published by the Free Software Foundation; either
       9             :  * version 2.1 of the License, or (at your option) any later version.
      10             :  *
      11             :  * FFmpeg is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with FFmpeg; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : /**
      22             :  * @file
      23             :  * video crop filter
      24             :  */
      25             : 
      26             : #include <stdio.h>
      27             : 
      28             : #include "avfilter.h"
      29             : #include "formats.h"
      30             : #include "internal.h"
      31             : #include "video.h"
      32             : #include "libavutil/eval.h"
      33             : #include "libavutil/avstring.h"
      34             : #include "libavutil/internal.h"
      35             : #include "libavutil/libm.h"
      36             : #include "libavutil/imgutils.h"
      37             : #include "libavutil/mathematics.h"
      38             : #include "libavutil/opt.h"
      39             : 
      40             : static const char *const var_names[] = {
      41             :     "in_w", "iw",   ///< width  of the input video
      42             :     "in_h", "ih",   ///< height of the input video
      43             :     "out_w", "ow",  ///< width  of the cropped video
      44             :     "out_h", "oh",  ///< height of the cropped video
      45             :     "a",
      46             :     "sar",
      47             :     "dar",
      48             :     "hsub",
      49             :     "vsub",
      50             :     "x",
      51             :     "y",
      52             :     "n",            ///< number of frame
      53             :     "pos",          ///< position in the file
      54             :     "t",            ///< timestamp expressed in seconds
      55             :     NULL
      56             : };
      57             : 
      58             : enum var_name {
      59             :     VAR_IN_W,  VAR_IW,
      60             :     VAR_IN_H,  VAR_IH,
      61             :     VAR_OUT_W, VAR_OW,
      62             :     VAR_OUT_H, VAR_OH,
      63             :     VAR_A,
      64             :     VAR_SAR,
      65             :     VAR_DAR,
      66             :     VAR_HSUB,
      67             :     VAR_VSUB,
      68             :     VAR_X,
      69             :     VAR_Y,
      70             :     VAR_N,
      71             :     VAR_POS,
      72             :     VAR_T,
      73             :     VAR_VARS_NB
      74             : };
      75             : 
      76             : typedef struct CropContext {
      77             :     const AVClass *class;
      78             :     int  x;             ///< x offset of the non-cropped area with respect to the input area
      79             :     int  y;             ///< y offset of the non-cropped area with respect to the input area
      80             :     int  w;             ///< width of the cropped area
      81             :     int  h;             ///< height of the cropped area
      82             : 
      83             :     AVRational out_sar; ///< output sample aspect ratio
      84             :     int keep_aspect;    ///< keep display aspect ratio when cropping
      85             :     int exact;          ///< exact cropping, for subsampled formats
      86             : 
      87             :     int max_step[4];    ///< max pixel step for each plane, expressed as a number of bytes
      88             :     int hsub, vsub;     ///< chroma subsampling
      89             :     char *x_expr, *y_expr, *w_expr, *h_expr;
      90             :     AVExpr *x_pexpr, *y_pexpr;  /* parsed expressions for x and y */
      91             :     double var_values[VAR_VARS_NB];
      92             : } CropContext;
      93             : 
      94         145 : static int query_formats(AVFilterContext *ctx)
      95             : {
      96         145 :     AVFilterFormats *formats = NULL;
      97             :     int fmt, ret;
      98             : 
      99       26680 :     for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) {
     100       26535 :         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
     101       50315 :         if (!(desc->flags & (AV_PIX_FMT_FLAG_HWACCEL | AV_PIX_FMT_FLAG_BITSTREAM)) &&
     102       62930 :             !((desc->log2_chroma_w || desc->log2_chroma_h) && !(desc->flags & AV_PIX_FMT_FLAG_PLANAR)) &&
     103       23200 :             (ret = ff_add_format(&formats, fmt)) < 0)
     104           0 :             return ret;
     105             :     }
     106             : 
     107         145 :     return ff_set_common_formats(ctx, formats);
     108             : }
     109             : 
     110         145 : static av_cold void uninit(AVFilterContext *ctx)
     111             : {
     112         145 :     CropContext *s = ctx->priv;
     113             : 
     114         145 :     av_expr_free(s->x_pexpr);
     115         145 :     s->x_pexpr = NULL;
     116         145 :     av_expr_free(s->y_pexpr);
     117         145 :     s->y_pexpr = NULL;
     118         145 : }
     119             : 
     120         640 : static inline int normalize_double(int *n, double d)
     121             : {
     122         640 :     int ret = 0;
     123             : 
     124         640 :     if (isnan(d)) {
     125           0 :         ret = AVERROR(EINVAL);
     126         640 :     } else if (d > INT_MAX || d < INT_MIN) {
     127           0 :         *n = d > INT_MAX ? INT_MAX : INT_MIN;
     128           0 :         ret = AVERROR(EINVAL);
     129             :     } else
     130         640 :         *n = lrint(d);
     131             : 
     132         640 :     return ret;
     133             : }
     134             : 
     135         144 : static int config_input(AVFilterLink *link)
     136             : {
     137         144 :     AVFilterContext *ctx = link->dst;
     138         144 :     CropContext *s = ctx->priv;
     139         144 :     const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(link->format);
     140             :     int ret;
     141             :     const char *expr;
     142             :     double res;
     143             : 
     144         144 :     s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = ctx->inputs[0]->w;
     145         144 :     s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = ctx->inputs[0]->h;
     146         144 :     s->var_values[VAR_A]     = (float) link->w / link->h;
     147         144 :     s->var_values[VAR_SAR]   = link->sample_aspect_ratio.num ? av_q2d(link->sample_aspect_ratio) : 1;
     148         144 :     s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
     149         144 :     s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
     150         144 :     s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
     151         144 :     s->var_values[VAR_X]     = NAN;
     152         144 :     s->var_values[VAR_Y]     = NAN;
     153         144 :     s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
     154         144 :     s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
     155         144 :     s->var_values[VAR_N]     = 0;
     156         144 :     s->var_values[VAR_T]     = NAN;
     157         144 :     s->var_values[VAR_POS]   = NAN;
     158             : 
     159         144 :     av_image_fill_max_pixsteps(s->max_step, NULL, pix_desc);
     160         144 :     s->hsub = pix_desc->log2_chroma_w;
     161         144 :     s->vsub = pix_desc->log2_chroma_h;
     162             : 
     163         144 :     if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
     164         144 :                                       var_names, s->var_values,
     165             :                                       NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
     166           0 :         goto fail_expr;
     167         144 :     s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = res;
     168         144 :     if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr),
     169         144 :                                       var_names, s->var_values,
     170             :                                       NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
     171           0 :         goto fail_expr;
     172         144 :     s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = res;
     173             :     /* evaluate again ow as it may depend on oh */
     174         144 :     if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
     175         144 :                                       var_names, s->var_values,
     176             :                                       NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
     177           0 :         goto fail_expr;
     178             : 
     179         144 :     s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = res;
     180         288 :     if (normalize_double(&s->w, s->var_values[VAR_OUT_W]) < 0 ||
     181         144 :         normalize_double(&s->h, s->var_values[VAR_OUT_H]) < 0) {
     182           0 :         av_log(ctx, AV_LOG_ERROR,
     183             :                "Too big value or invalid expression for out_w/ow or out_h/oh. "
     184             :                "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n",
     185             :                s->w_expr, s->h_expr);
     186           0 :         return AVERROR(EINVAL);
     187             :     }
     188             : 
     189         144 :     if (!s->exact) {
     190         144 :         s->w &= ~((1 << s->hsub) - 1);
     191         144 :         s->h &= ~((1 << s->vsub) - 1);
     192             :     }
     193             : 
     194         144 :     av_expr_free(s->x_pexpr);
     195         144 :     av_expr_free(s->y_pexpr);
     196         144 :     s->x_pexpr = s->y_pexpr = NULL;
     197         144 :     if ((ret = av_expr_parse(&s->x_pexpr, s->x_expr, var_names,
     198         144 :                              NULL, NULL, NULL, NULL, 0, ctx)) < 0 ||
     199         144 :         (ret = av_expr_parse(&s->y_pexpr, s->y_expr, var_names,
     200             :                              NULL, NULL, NULL, NULL, 0, ctx)) < 0)
     201           0 :         return AVERROR(EINVAL);
     202             : 
     203         144 :     if (s->keep_aspect) {
     204           0 :         AVRational dar = av_mul_q(link->sample_aspect_ratio,
     205           0 :                                   (AVRational){ link->w, link->h });
     206           0 :         av_reduce(&s->out_sar.num, &s->out_sar.den,
     207           0 :                   dar.num * s->h, dar.den * s->w, INT_MAX);
     208             :     } else
     209         144 :         s->out_sar = link->sample_aspect_ratio;
     210             : 
     211         144 :     av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d sar:%d/%d -> w:%d h:%d sar:%d/%d\n",
     212             :            link->w, link->h, link->sample_aspect_ratio.num, link->sample_aspect_ratio.den,
     213             :            s->w, s->h, s->out_sar.num, s->out_sar.den);
     214             : 
     215         288 :     if (s->w <= 0 || s->h <= 0 ||
     216         288 :         s->w > link->w || s->h > link->h) {
     217           0 :         av_log(ctx, AV_LOG_ERROR,
     218             :                "Invalid too big or non positive size for width '%d' or height '%d'\n",
     219             :                s->w, s->h);
     220           0 :         return AVERROR(EINVAL);
     221             :     }
     222             : 
     223             :     /* set default, required in the case the first computed value for x/y is NAN */
     224         144 :     s->x = (link->w - s->w) / 2;
     225         144 :     s->y = (link->h - s->h) / 2;
     226         144 :     if (!s->exact) {
     227         144 :         s->x &= ~((1 << s->hsub) - 1);
     228         144 :         s->y &= ~((1 << s->vsub) - 1);
     229             :     }
     230         144 :     return 0;
     231             : 
     232           0 : fail_expr:
     233           0 :     av_log(NULL, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr);
     234           0 :     return ret;
     235             : }
     236             : 
     237         144 : static int config_output(AVFilterLink *link)
     238             : {
     239         144 :     CropContext *s = link->src->priv;
     240             : 
     241         144 :     link->w = s->w;
     242         144 :     link->h = s->h;
     243         144 :     link->sample_aspect_ratio = s->out_sar;
     244             : 
     245         144 :     return 0;
     246             : }
     247             : 
     248         176 : static int filter_frame(AVFilterLink *link, AVFrame *frame)
     249             : {
     250         176 :     AVFilterContext *ctx = link->dst;
     251         176 :     CropContext *s = ctx->priv;
     252         176 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
     253             :     int i;
     254             : 
     255         176 :     frame->width  = s->w;
     256         176 :     frame->height = s->h;
     257             : 
     258         176 :     s->var_values[VAR_N] = link->frame_count_out;
     259         352 :     s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
     260         176 :         NAN : frame->pts * av_q2d(link->time_base);
     261         352 :     s->var_values[VAR_POS] = frame->pkt_pos == -1 ?
     262         176 :         NAN : frame->pkt_pos;
     263         176 :     s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
     264         176 :     s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
     265         176 :     s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
     266             : 
     267         176 :     normalize_double(&s->x, s->var_values[VAR_X]);
     268         176 :     normalize_double(&s->y, s->var_values[VAR_Y]);
     269             : 
     270         176 :     if (s->x < 0)
     271           0 :         s->x = 0;
     272         176 :     if (s->y < 0)
     273           0 :         s->y = 0;
     274         176 :     if ((unsigned)s->x + (unsigned)s->w > link->w)
     275           0 :         s->x = link->w - s->w;
     276         176 :     if ((unsigned)s->y + (unsigned)s->h > link->h)
     277           0 :         s->y = link->h - s->h;
     278         176 :     if (!s->exact) {
     279         176 :         s->x &= ~((1 << s->hsub) - 1);
     280         176 :         s->y &= ~((1 << s->vsub) - 1);
     281             :     }
     282             : 
     283         528 :     av_log(ctx, AV_LOG_TRACE, "n:%d t:%f pos:%f x:%d y:%d x+w:%d y+h:%d\n",
     284         176 :             (int)s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
     285         352 :             s->x, s->y, s->x+s->w, s->y+s->h);
     286             : 
     287         176 :     frame->data[0] += s->y * frame->linesize[0];
     288         176 :     frame->data[0] += s->x * s->max_step[0];
     289             : 
     290         176 :     if (!(desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)) {
     291         510 :         for (i = 1; i < 3; i ++) {
     292         340 :             if (frame->data[i]) {
     293         252 :                 frame->data[i] += (s->y >> s->vsub) * frame->linesize[i];
     294         252 :                 frame->data[i] += (s->x * s->max_step[i]) >> s->hsub;
     295             :             }
     296             :         }
     297             :     }
     298             : 
     299             :     /* alpha plane */
     300         176 :     if (frame->data[3]) {
     301          28 :         frame->data[3] += s->y * frame->linesize[3];
     302          28 :         frame->data[3] += s->x * s->max_step[3];
     303             :     }
     304             : 
     305         176 :     return ff_filter_frame(link->dst->outputs[0], frame);
     306             : }
     307             : 
     308           0 : static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
     309             :                            char *res, int res_len, int flags)
     310             : {
     311           0 :     CropContext *s = ctx->priv;
     312             :     int ret;
     313             : 
     314           0 :     if (   !strcmp(cmd, "out_w")  || !strcmp(cmd, "w")
     315           0 :         || !strcmp(cmd, "out_h")  || !strcmp(cmd, "h")
     316           0 :         || !strcmp(cmd, "x")      || !strcmp(cmd, "y")) {
     317             : 
     318           0 :         int old_x = s->x;
     319           0 :         int old_y = s->y;
     320           0 :         int old_w = s->w;
     321           0 :         int old_h = s->h;
     322             : 
     323           0 :         AVFilterLink *outlink = ctx->outputs[0];
     324           0 :         AVFilterLink *inlink  = ctx->inputs[0];
     325             : 
     326           0 :         av_opt_set(s, cmd, args, 0);
     327             : 
     328           0 :         if ((ret = config_input(inlink)) < 0) {
     329           0 :             s->x = old_x;
     330           0 :             s->y = old_y;
     331           0 :             s->w = old_w;
     332           0 :             s->h = old_h;
     333           0 :             return ret;
     334             :         }
     335             : 
     336           0 :         ret = config_output(outlink);
     337             : 
     338             :     } else
     339           0 :         ret = AVERROR(ENOSYS);
     340             : 
     341           0 :     return ret;
     342             : }
     343             : 
     344             : #define OFFSET(x) offsetof(CropContext, x)
     345             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
     346             : 
     347             : static const AVOption crop_options[] = {
     348             :     { "out_w",       "set the width crop area expression",   OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
     349             :     { "w",           "set the width crop area expression",   OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
     350             :     { "out_h",       "set the height crop area expression",  OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
     351             :     { "h",           "set the height crop area expression",  OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
     352             :     { "x",           "set the x crop area expression",       OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "(in_w-out_w)/2"}, CHAR_MIN, CHAR_MAX, FLAGS },
     353             :     { "y",           "set the y crop area expression",       OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "(in_h-out_h)/2"}, CHAR_MIN, CHAR_MAX, FLAGS },
     354             :     { "keep_aspect", "keep aspect ratio",                    OFFSET(keep_aspect), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
     355             :     { "exact",       "do exact cropping",                    OFFSET(exact),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
     356             :     { NULL }
     357             : };
     358             : 
     359             : AVFILTER_DEFINE_CLASS(crop);
     360             : 
     361             : static const AVFilterPad avfilter_vf_crop_inputs[] = {
     362             :     {
     363             :         .name         = "default",
     364             :         .type         = AVMEDIA_TYPE_VIDEO,
     365             :         .filter_frame = filter_frame,
     366             :         .config_props = config_input,
     367             :     },
     368             :     { NULL }
     369             : };
     370             : 
     371             : static const AVFilterPad avfilter_vf_crop_outputs[] = {
     372             :     {
     373             :         .name         = "default",
     374             :         .type         = AVMEDIA_TYPE_VIDEO,
     375             :         .config_props = config_output,
     376             :     },
     377             :     { NULL }
     378             : };
     379             : 
     380             : AVFilter ff_vf_crop = {
     381             :     .name            = "crop",
     382             :     .description     = NULL_IF_CONFIG_SMALL("Crop the input video."),
     383             :     .priv_size       = sizeof(CropContext),
     384             :     .priv_class      = &crop_class,
     385             :     .query_formats   = query_formats,
     386             :     .uninit          = uninit,
     387             :     .inputs          = avfilter_vf_crop_inputs,
     388             :     .outputs         = avfilter_vf_crop_outputs,
     389             :     .process_command = process_command,
     390             : };

Generated by: LCOV version 1.13