vf_crop.c 11.8 KB
Newer Older
Stefano Sabatini's avatar
Stefano Sabatini committed
1
/*
2
 * Copyright (c) 2007 Bobby Bingham
Stefano Sabatini's avatar
Stefano Sabatini committed
3
 *
4
 * This file is part of Libav.
Stefano Sabatini's avatar
Stefano Sabatini committed
5
 *
6
 * Libav is free software; you can redistribute it and/or
Stefano Sabatini's avatar
Stefano Sabatini committed
7 8 9 10
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
11
 * Libav is distributed in the hope that it will be useful,
Stefano Sabatini's avatar
Stefano Sabatini committed
12 13 14 15 16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with Libav; if not, write to the Free Software
Stefano Sabatini's avatar
Stefano Sabatini committed
18 19 20 21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
22
 * @file
Stefano Sabatini's avatar
Stefano Sabatini committed
23 24 25
 * video crop filter
 */

26 27
/* #define DEBUG */

Stefano Sabatini's avatar
Stefano Sabatini committed
28
#include "avfilter.h"
29 30 31
#include "libavutil/eval.h"
#include "libavutil/avstring.h"
#include "libavutil/libm.h"
32
#include "libavutil/imgutils.h"
Stefano Sabatini's avatar
Stefano Sabatini committed
33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
static const char *var_names[] = {
    "E",
    "PHI",
    "PI",
    "in_w", "iw",   ///< width  of the input video
    "in_h", "ih",   ///< height of the input video
    "out_w", "ow",  ///< width  of the cropped video
    "out_h", "oh",  ///< height of the cropped video
    "x",
    "y",
    "n",            ///< number of frame
    "pos",          ///< position in the file
    "t",            ///< timestamp expressed in seconds
    NULL
};

enum var_name {
51 52 53 54 55 56 57 58 59 60 61 62 63
    VAR_E,
    VAR_PHI,
    VAR_PI,
    VAR_IN_W,  VAR_IW,
    VAR_IN_H,  VAR_IH,
    VAR_OUT_W, VAR_OW,
    VAR_OUT_H, VAR_OH,
    VAR_X,
    VAR_Y,
    VAR_N,
    VAR_POS,
    VAR_T,
    VAR_VARS_NB
64 65
};

Stefano Sabatini's avatar
Stefano Sabatini committed
66 67 68 69 70 71
typedef struct {
    int  x;             ///< x offset of the non-cropped area with respect to the input area
    int  y;             ///< y offset of the non-cropped area with respect to the input area
    int  w;             ///< width of the cropped area
    int  h;             ///< height of the cropped area

72
    int max_step[4];    ///< max pixel step for each plane, expressed as a number of bytes
Stefano Sabatini's avatar
Stefano Sabatini committed
73
    int hsub, vsub;     ///< chroma subsampling
74 75
    char x_expr[256], y_expr[256], ow_expr[256], oh_expr[256];
    AVExpr *x_pexpr, *y_pexpr;  /* parsed expressions for x and y */
76
    double var_values[VAR_VARS_NB];
Stefano Sabatini's avatar
Stefano Sabatini committed
77 78 79 80
} CropContext;

static int query_formats(AVFilterContext *ctx)
{
81
    static const enum PixelFormat pix_fmts[] = {
Stefano Sabatini's avatar
Stefano Sabatini committed
82
        PIX_FMT_RGB48BE,      PIX_FMT_RGB48LE,
83
        PIX_FMT_BGR48BE,      PIX_FMT_BGR48LE,
Stefano Sabatini's avatar
Stefano Sabatini committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
        PIX_FMT_ARGB,         PIX_FMT_RGBA,
        PIX_FMT_ABGR,         PIX_FMT_BGRA,
        PIX_FMT_RGB24,        PIX_FMT_BGR24,
        PIX_FMT_RGB565BE,     PIX_FMT_RGB565LE,
        PIX_FMT_RGB555BE,     PIX_FMT_RGB555LE,
        PIX_FMT_BGR565BE,     PIX_FMT_BGR565LE,
        PIX_FMT_BGR555BE,     PIX_FMT_BGR555LE,
        PIX_FMT_GRAY16BE,     PIX_FMT_GRAY16LE,
        PIX_FMT_YUV420P16LE,  PIX_FMT_YUV420P16BE,
        PIX_FMT_YUV422P16LE,  PIX_FMT_YUV422P16BE,
        PIX_FMT_YUV444P16LE,  PIX_FMT_YUV444P16BE,
        PIX_FMT_YUV444P,      PIX_FMT_YUV422P,
        PIX_FMT_YUV420P,      PIX_FMT_YUV411P,
        PIX_FMT_YUV410P,      PIX_FMT_YUV440P,
        PIX_FMT_YUVJ444P,     PIX_FMT_YUVJ422P,
        PIX_FMT_YUVJ420P,     PIX_FMT_YUVJ440P,
        PIX_FMT_YUVA420P,
        PIX_FMT_RGB8,         PIX_FMT_BGR8,
        PIX_FMT_RGB4_BYTE,    PIX_FMT_BGR4_BYTE,
        PIX_FMT_PAL8,         PIX_FMT_GRAY8,
        PIX_FMT_NONE
    };

    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));

    return 0;
}

static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
{
    CropContext *crop = ctx->priv;

116 117 118 119 120
    av_strlcpy(crop->ow_expr, "iw", sizeof(crop->ow_expr));
    av_strlcpy(crop->oh_expr, "ih", sizeof(crop->oh_expr));
    av_strlcpy(crop->x_expr, "(in_w-out_w)/2", sizeof(crop->x_expr));
    av_strlcpy(crop->y_expr, "(in_h-out_h)/2", sizeof(crop->y_expr));

Stefano Sabatini's avatar
Stefano Sabatini committed
121
    if (args)
122
        sscanf(args, "%255[^:]:%255[^:]:%255[^:]:%255[^:]", crop->ow_expr, crop->oh_expr, crop->x_expr, crop->y_expr);
Stefano Sabatini's avatar
Stefano Sabatini committed
123 124 125 126

    return 0;
}

127 128 129 130
static av_cold void uninit(AVFilterContext *ctx)
{
    CropContext *crop = ctx->priv;

131 132
    av_expr_free(crop->x_pexpr); crop->x_pexpr = NULL;
    av_expr_free(crop->y_pexpr); crop->y_pexpr = NULL;
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
}

static inline int normalize_double(int *n, double d)
{
    int ret = 0;

    if (isnan(d)) {
        ret = AVERROR(EINVAL);
    } else if (d > INT_MAX || d < INT_MIN) {
        *n = d > INT_MAX ? INT_MAX : INT_MIN;
        ret = AVERROR(EINVAL);
    } else
        *n = round(d);

    return ret;
}

Stefano Sabatini's avatar
Stefano Sabatini committed
150 151 152 153
static int config_input(AVFilterLink *link)
{
    AVFilterContext *ctx = link->dst;
    CropContext *crop = ctx->priv;
154
    const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[link->format];
155 156 157 158
    int ret;
    const char *expr;
    double res;

159 160 161 162 163 164 165 166 167 168 169 170
    crop->var_values[VAR_E]     = M_E;
    crop->var_values[VAR_PHI]   = M_PHI;
    crop->var_values[VAR_PI]    = M_PI;
    crop->var_values[VAR_IN_W]  = crop->var_values[VAR_IW] = ctx->inputs[0]->w;
    crop->var_values[VAR_IN_H]  = crop->var_values[VAR_IH] = ctx->inputs[0]->h;
    crop->var_values[VAR_X]     = NAN;
    crop->var_values[VAR_Y]     = NAN;
    crop->var_values[VAR_OUT_W] = crop->var_values[VAR_OW] = NAN;
    crop->var_values[VAR_OUT_H] = crop->var_values[VAR_OH] = NAN;
    crop->var_values[VAR_N]     = 0;
    crop->var_values[VAR_T]     = NAN;
    crop->var_values[VAR_POS]   = NAN;
Stefano Sabatini's avatar
Stefano Sabatini committed
171

172
    av_image_fill_max_pixsteps(crop->max_step, NULL, pix_desc);
173 174
    crop->hsub = av_pix_fmt_descriptors[link->format].log2_chroma_w;
    crop->vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h;
Stefano Sabatini's avatar
Stefano Sabatini committed
175

176
    if ((ret = av_expr_parse_and_eval(&res, (expr = crop->ow_expr),
177 178
                                      var_names, crop->var_values,
                                      NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) goto fail_expr;
179
    crop->var_values[VAR_OUT_W] = crop->var_values[VAR_OW] = res;
180
    if ((ret = av_expr_parse_and_eval(&res, (expr = crop->oh_expr),
181 182
                                      var_names, crop->var_values,
                                      NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) goto fail_expr;
183
    crop->var_values[VAR_OUT_H] = crop->var_values[VAR_OH] = res;
184
    /* evaluate again ow as it may depend on oh */
185
    if ((ret = av_expr_parse_and_eval(&res, (expr = crop->ow_expr),
186 187
                                      var_names, crop->var_values,
                                      NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) goto fail_expr;
188 189 190
    crop->var_values[VAR_OUT_W] = crop->var_values[VAR_OW] = res;
    if (normalize_double(&crop->w, crop->var_values[VAR_OUT_W]) < 0 ||
        normalize_double(&crop->h, crop->var_values[VAR_OUT_H]) < 0) {
191 192 193 194 195 196 197 198
        av_log(ctx, AV_LOG_ERROR,
               "Too big value or invalid expression for out_w/ow or out_h/oh. "
               "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n",
               crop->ow_expr, crop->oh_expr);
        return AVERROR(EINVAL);
    }
    crop->w &= ~((1 << crop->hsub) - 1);
    crop->h &= ~((1 << crop->vsub) - 1);
Stefano Sabatini's avatar
Stefano Sabatini committed
199

200
    if ((ret = av_expr_parse(&crop->x_pexpr, crop->x_expr, var_names,
201
                             NULL, NULL, NULL, NULL, 0, ctx)) < 0 ||
202
        (ret = av_expr_parse(&crop->y_pexpr, crop->y_expr, var_names,
203 204
                             NULL, NULL, NULL, NULL, 0, ctx)) < 0)
        return AVERROR(EINVAL);
Stefano Sabatini's avatar
Stefano Sabatini committed
205

206
    av_log(ctx, AV_LOG_INFO, "w:%d h:%d -> w:%d h:%d\n",
207
           link->w, link->h, crop->w, crop->h);
208

209 210
    if (crop->w <= 0 || crop->h <= 0 ||
        crop->w > link->w || crop->h > link->h) {
Stefano Sabatini's avatar
Stefano Sabatini committed
211
        av_log(ctx, AV_LOG_ERROR,
212 213
               "Invalid too big or non positive size for width '%d' or height '%d'\n",
               crop->w, crop->h);
214
        return AVERROR(EINVAL);
Stefano Sabatini's avatar
Stefano Sabatini committed
215 216
    }

217 218 219 220 221
    /* set default, required in the case the first computed value for x/y is NAN */
    crop->x = (link->w - crop->w) / 2;
    crop->y = (link->h - crop->h) / 2;
    crop->x &= ~((1 << crop->hsub) - 1);
    crop->y &= ~((1 << crop->vsub) - 1);
Stefano Sabatini's avatar
Stefano Sabatini committed
222
    return 0;
223 224 225 226

fail_expr:
    av_log(NULL, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr);
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
227 228 229 230 231 232 233 234 235 236 237 238
}

static int config_output(AVFilterLink *link)
{
    CropContext *crop = link->src->priv;

    link->w = crop->w;
    link->h = crop->h;

    return 0;
}

239
static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
Stefano Sabatini's avatar
Stefano Sabatini committed
240
{
241 242
    AVFilterContext *ctx = link->dst;
    CropContext *crop = ctx->priv;
243
    AVFilterBufferRef *ref2;
Stefano Sabatini's avatar
Stefano Sabatini committed
244 245
    int i;

246
    ref2 = avfilter_ref_buffer(picref, ~0);
247 248
    ref2->video->w = crop->w;
    ref2->video->h = crop->h;
249

250 251
    crop->var_values[VAR_T] = picref->pts == AV_NOPTS_VALUE ?
        NAN : picref->pts * av_q2d(link->time_base);
252
    crop->var_values[VAR_POS] = picref->pos == -1 ? NAN : picref->pos;
253 254 255
    crop->var_values[VAR_X] = av_expr_eval(crop->x_pexpr, crop->var_values, NULL);
    crop->var_values[VAR_Y] = av_expr_eval(crop->y_pexpr, crop->var_values, NULL);
    crop->var_values[VAR_X] = av_expr_eval(crop->x_pexpr, crop->var_values, NULL);
256

257 258
    normalize_double(&crop->x, crop->var_values[VAR_X]);
    normalize_double(&crop->y, crop->var_values[VAR_Y]);
259 260 261 262 263 264 265 266

    if (crop->x < 0) crop->x = 0;
    if (crop->y < 0) crop->y = 0;
    if ((unsigned)crop->x + (unsigned)crop->w > link->w) crop->x = link->w - crop->w;
    if ((unsigned)crop->y + (unsigned)crop->h > link->h) crop->y = link->h - crop->h;
    crop->x &= ~((1 << crop->hsub) - 1);
    crop->y &= ~((1 << crop->vsub) - 1);

267
#ifdef DEBUG
268 269
    av_log(ctx, AV_LOG_DEBUG,
           "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n",
270
           (int)crop->var_values[VAR_N], crop->var_values[VAR_T], crop->x, crop->y, crop->x+crop->w, crop->y+crop->h);
271
#endif
272

Stefano Sabatini's avatar
Stefano Sabatini committed
273
    ref2->data[0] += crop->y * ref2->linesize[0];
274
    ref2->data[0] += crop->x * crop->max_step[0];
Stefano Sabatini's avatar
Stefano Sabatini committed
275

276
    if (!(av_pix_fmt_descriptors[link->format].flags & PIX_FMT_PAL)) {
Stefano Sabatini's avatar
Stefano Sabatini committed
277 278 279
        for (i = 1; i < 3; i ++) {
            if (ref2->data[i]) {
                ref2->data[i] += (crop->y >> crop->vsub) * ref2->linesize[i];
280
                ref2->data[i] += (crop->x * crop->max_step[i]) >> crop->hsub;
Stefano Sabatini's avatar
Stefano Sabatini committed
281 282 283 284 285 286 287
            }
        }
    }

    /* alpha plane */
    if (ref2->data[3]) {
        ref2->data[3] += crop->y * ref2->linesize[3];
288
        ref2->data[3] += crop->x * crop->max_step[3];
Stefano Sabatini's avatar
Stefano Sabatini committed
289 290 291 292 293
    }

    avfilter_start_frame(link->dst->outputs[0], ref2);
}

294
static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
Stefano Sabatini's avatar
Stefano Sabatini committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308
{
    AVFilterContext *ctx = link->dst;
    CropContext *crop = ctx->priv;

    if (y >= crop->y + crop->h || y + h <= crop->y)
        return;

    if (y < crop->y) {
        h -= crop->y - y;
        y  = crop->y;
    }
    if (y + h > crop->y + crop->h)
        h = crop->y + crop->h - y;

309
    avfilter_draw_slice(ctx->outputs[0], y - crop->y, h, slice_dir);
Stefano Sabatini's avatar
Stefano Sabatini committed
310 311
}

312 313 314 315
static void end_frame(AVFilterLink *link)
{
    CropContext *crop = link->dst->priv;

316
    crop->var_values[VAR_N] += 1.0;
Stefano Sabatini's avatar
Stefano Sabatini committed
317
    avfilter_unref_buffer(link->cur_buf);
318 319 320
    avfilter_end_frame(link->dst->outputs[0]);
}

Stefano Sabatini's avatar
Stefano Sabatini committed
321 322
AVFilter avfilter_vf_crop = {
    .name      = "crop",
323
    .description = NULL_IF_CONFIG_SMALL("Crop the input video to width:height:x:y."),
324

Stefano Sabatini's avatar
Stefano Sabatini committed
325 326 327 328
    .priv_size = sizeof(CropContext),

    .query_formats = query_formats,
    .init          = init,
329
    .uninit        = uninit,
Stefano Sabatini's avatar
Stefano Sabatini committed
330 331

    .inputs    = (AVFilterPad[]) {{ .name             = "default",
332
                                    .type             = AVMEDIA_TYPE_VIDEO,
Stefano Sabatini's avatar
Stefano Sabatini committed
333 334
                                    .start_frame      = start_frame,
                                    .draw_slice       = draw_slice,
335
                                    .end_frame        = end_frame,
336
                                    .get_video_buffer = avfilter_null_get_video_buffer,
Stefano Sabatini's avatar
Stefano Sabatini committed
337 338 339
                                    .config_props     = config_input, },
                                  { .name = NULL}},
    .outputs   = (AVFilterPad[]) {{ .name             = "default",
340
                                    .type             = AVMEDIA_TYPE_VIDEO,
Stefano Sabatini's avatar
Stefano Sabatini committed
341 342 343
                                    .config_props     = config_output, },
                                  { .name = NULL}},
};