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: 2018-05-20 11:54:08 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         149 : static int query_formats(AVFilterContext *ctx)
      95             : {
      96         149 :     AVFilterFormats *formats = NULL;
      97             :     int fmt, ret;
      98             : 
      99       27714 :     for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) {
     100       27565 :         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
     101       52299 :         if (!(desc->flags & (AV_PIX_FMT_FLAG_HWACCEL | AV_PIX_FMT_FLAG_BITSTREAM)) &&
     102       65560 :             !((desc->log2_chroma_w || desc->log2_chroma_h) && !(desc->flags & AV_PIX_FMT_FLAG_PLANAR)) &&
     103       24138 :             (ret = ff_add_format(&formats, fmt)) < 0)
     104           0 :             return ret;
     105             :     }
     106             : 
     107         149 :     return ff_set_common_formats(ctx, formats);
     108             : }
     109             : 
     110         149 : static av_cold void uninit(AVFilterContext *ctx)
     111             : {
     112         149 :     CropContext *s = ctx->priv;
     113             : 
     114         149 :     av_expr_free(s->x_pexpr);
     115         149 :     s->x_pexpr = NULL;
     116         149 :     av_expr_free(s->y_pexpr);
     117         149 :     s->y_pexpr = NULL;
     118         149 : }
     119             : 
     120         656 : static inline int normalize_double(int *n, double d)
     121             : {
     122         656 :     int ret = 0;
     123             : 
     124         656 :     if (isnan(d)) {
     125           0 :         ret = AVERROR(EINVAL);
     126         656 :     } 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         656 :         *n = lrint(d);
     131             : 
     132         656 :     return ret;
     133             : }
     134             : 
     135         148 : static int config_input(AVFilterLink *link)
     136             : {
     137         148 :     AVFilterContext *ctx = link->dst;
     138         148 :     CropContext *s = ctx->priv;
     139         148 :     const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(link->format);
     140             :     int ret;
     141             :     const char *expr;
     142             :     double res;
     143             : 
     144         148 :     s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = ctx->inputs[0]->w;
     145         148 :     s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = ctx->inputs[0]->h;
     146         148 :     s->var_values[VAR_A]     = (float) link->w / link->h;
     147         148 :     s->var_values[VAR_SAR]   = link->sample_aspect_ratio.num ? av_q2d(link->sample_aspect_ratio) : 1;
     148         148 :     s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
     149         148 :     s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
     150         148 :     s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
     151         148 :     s->var_values[VAR_X]     = NAN;
     152         148 :     s->var_values[VAR_Y]     = NAN;
     153         148 :     s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
     154         148 :     s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
     155         148 :     s->var_values[VAR_N]     = 0;
     156         148 :     s->var_values[VAR_T]     = NAN;
     157         148 :     s->var_values[VAR_POS]   = NAN;
     158             : 
     159         148 :     av_image_fill_max_pixsteps(s->max_step, NULL, pix_desc);
     160         148 :     s->hsub = pix_desc->log2_chroma_w;
     161         148 :     s->vsub = pix_desc->log2_chroma_h;
     162             : 
     163         148 :     if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
     164         148 :                                       var_names, s->var_values,
     165             :                                       NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
     166           0 :         goto fail_expr;
     167         148 :     s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = res;
     168         148 :     if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr),
     169         148 :                                       var_names, s->var_values,
     170             :                                       NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
     171           0 :         goto fail_expr;
     172         148 :     s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = res;
     173             :     /* evaluate again ow as it may depend on oh */
     174         148 :     if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
     175         148 :                                       var_names, s->var_values,
     176             :                                       NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
     177           0 :         goto fail_expr;
     178             : 
     179         148 :     s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = res;
     180         296 :     if (normalize_double(&s->w, s->var_values[VAR_OUT_W]) < 0 ||
     181         148 :         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         148 :     if (!s->exact) {
     190         148 :         s->w &= ~((1 << s->hsub) - 1);
     191         148 :         s->h &= ~((1 << s->vsub) - 1);
     192             :     }
     193             : 
     194         148 :     av_expr_free(s->x_pexpr);
     195         148 :     av_expr_free(s->y_pexpr);
     196         148 :     s->x_pexpr = s->y_pexpr = NULL;
     197         148 :     if ((ret = av_expr_parse(&s->x_pexpr, s->x_expr, var_names,
     198         148 :                              NULL, NULL, NULL, NULL, 0, ctx)) < 0 ||
     199         148 :         (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         148 :     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         148 :         s->out_sar = link->sample_aspect_ratio;
     210             : 
     211         148 :     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         296 :     if (s->w <= 0 || s->h <= 0 ||
     216         296 :         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         148 :     s->x = (link->w - s->w) / 2;
     225         148 :     s->y = (link->h - s->h) / 2;
     226         148 :     if (!s->exact) {
     227         148 :         s->x &= ~((1 << s->hsub) - 1);
     228         148 :         s->y &= ~((1 << s->vsub) - 1);
     229             :     }
     230         148 :     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         148 : static int config_output(AVFilterLink *link)
     238             : {
     239         148 :     CropContext *s = link->src->priv;
     240             : 
     241         148 :     link->w = s->w;
     242         148 :     link->h = s->h;
     243         148 :     link->sample_aspect_ratio = s->out_sar;
     244             : 
     245         148 :     return 0;
     246             : }
     247             : 
     248         180 : static int filter_frame(AVFilterLink *link, AVFrame *frame)
     249             : {
     250         180 :     AVFilterContext *ctx = link->dst;
     251         180 :     CropContext *s = ctx->priv;
     252         180 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
     253             :     int i;
     254             : 
     255         180 :     frame->width  = s->w;
     256         180 :     frame->height = s->h;
     257             : 
     258         180 :     s->var_values[VAR_N] = link->frame_count_out;
     259         360 :     s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
     260         180 :         NAN : frame->pts * av_q2d(link->time_base);
     261         360 :     s->var_values[VAR_POS] = frame->pkt_pos == -1 ?
     262         180 :         NAN : frame->pkt_pos;
     263         180 :     s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
     264         180 :     s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
     265             :     /* It is necessary if x is expressed from y  */
     266         180 :     s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
     267             : 
     268         180 :     normalize_double(&s->x, s->var_values[VAR_X]);
     269         180 :     normalize_double(&s->y, s->var_values[VAR_Y]);
     270             : 
     271         180 :     if (s->x < 0)
     272           0 :         s->x = 0;
     273         180 :     if (s->y < 0)
     274           0 :         s->y = 0;
     275         180 :     if ((unsigned)s->x + (unsigned)s->w > link->w)
     276           0 :         s->x = link->w - s->w;
     277         180 :     if ((unsigned)s->y + (unsigned)s->h > link->h)
     278           0 :         s->y = link->h - s->h;
     279         180 :     if (!s->exact) {
     280         180 :         s->x &= ~((1 << s->hsub) - 1);
     281         180 :         s->y &= ~((1 << s->vsub) - 1);
     282             :     }
     283             : 
     284         540 :     av_log(ctx, AV_LOG_TRACE, "n:%d t:%f pos:%f x:%d y:%d x+w:%d y+h:%d\n",
     285         180 :             (int)s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
     286         360 :             s->x, s->y, s->x+s->w, s->y+s->h);
     287             : 
     288         180 :     frame->data[0] += s->y * frame->linesize[0];
     289         180 :     frame->data[0] += s->x * s->max_step[0];
     290             : 
     291         180 :     if (!(desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & FF_PSEUDOPAL)) {
     292         522 :         for (i = 1; i < 3; i ++) {
     293         348 :             if (frame->data[i]) {
     294         254 :                 frame->data[i] += (s->y >> s->vsub) * frame->linesize[i];
     295         254 :                 frame->data[i] += (s->x * s->max_step[i]) >> s->hsub;
     296             :             }
     297             :         }
     298             :     }
     299             : 
     300             :     /* alpha plane */
     301         180 :     if (frame->data[3]) {
     302          28 :         frame->data[3] += s->y * frame->linesize[3];
     303          28 :         frame->data[3] += s->x * s->max_step[3];
     304             :     }
     305             : 
     306         180 :     return ff_filter_frame(link->dst->outputs[0], frame);
     307             : }
     308             : 
     309           0 : static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
     310             :                            char *res, int res_len, int flags)
     311             : {
     312           0 :     CropContext *s = ctx->priv;
     313             :     int ret;
     314             : 
     315           0 :     if (   !strcmp(cmd, "out_w")  || !strcmp(cmd, "w")
     316           0 :         || !strcmp(cmd, "out_h")  || !strcmp(cmd, "h")
     317           0 :         || !strcmp(cmd, "x")      || !strcmp(cmd, "y")) {
     318             : 
     319           0 :         int old_x = s->x;
     320           0 :         int old_y = s->y;
     321           0 :         int old_w = s->w;
     322           0 :         int old_h = s->h;
     323             : 
     324           0 :         AVFilterLink *outlink = ctx->outputs[0];
     325           0 :         AVFilterLink *inlink  = ctx->inputs[0];
     326             : 
     327           0 :         av_opt_set(s, cmd, args, 0);
     328             : 
     329           0 :         if ((ret = config_input(inlink)) < 0) {
     330           0 :             s->x = old_x;
     331           0 :             s->y = old_y;
     332           0 :             s->w = old_w;
     333           0 :             s->h = old_h;
     334           0 :             return ret;
     335             :         }
     336             : 
     337           0 :         ret = config_output(outlink);
     338             : 
     339             :     } else
     340           0 :         ret = AVERROR(ENOSYS);
     341             : 
     342           0 :     return ret;
     343             : }
     344             : 
     345             : #define OFFSET(x) offsetof(CropContext, x)
     346             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
     347             : 
     348             : static const AVOption crop_options[] = {
     349             :     { "out_w",       "set the width crop area expression",   OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
     350             :     { "w",           "set the width crop area expression",   OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
     351             :     { "out_h",       "set the height crop area expression",  OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
     352             :     { "h",           "set the height crop area expression",  OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
     353             :     { "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 },
     354             :     { "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 },
     355             :     { "keep_aspect", "keep aspect ratio",                    OFFSET(keep_aspect), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
     356             :     { "exact",       "do exact cropping",                    OFFSET(exact),  AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
     357             :     { NULL }
     358             : };
     359             : 
     360             : AVFILTER_DEFINE_CLASS(crop);
     361             : 
     362             : static const AVFilterPad avfilter_vf_crop_inputs[] = {
     363             :     {
     364             :         .name         = "default",
     365             :         .type         = AVMEDIA_TYPE_VIDEO,
     366             :         .filter_frame = filter_frame,
     367             :         .config_props = config_input,
     368             :     },
     369             :     { NULL }
     370             : };
     371             : 
     372             : static const AVFilterPad avfilter_vf_crop_outputs[] = {
     373             :     {
     374             :         .name         = "default",
     375             :         .type         = AVMEDIA_TYPE_VIDEO,
     376             :         .config_props = config_output,
     377             :     },
     378             :     { NULL }
     379             : };
     380             : 
     381             : AVFilter ff_vf_crop = {
     382             :     .name            = "crop",
     383             :     .description     = NULL_IF_CONFIG_SMALL("Crop the input video."),
     384             :     .priv_size       = sizeof(CropContext),
     385             :     .priv_class      = &crop_class,
     386             :     .query_formats   = query_formats,
     387             :     .uninit          = uninit,
     388             :     .inputs          = avfilter_vf_crop_inputs,
     389             :     .outputs         = avfilter_vf_crop_outputs,
     390             :     .process_command = process_command,
     391             : };

Generated by: LCOV version 1.13