00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00026
00027
00028 #include <stdio.h>
00029
00030 #include "avfilter.h"
00031 #include "formats.h"
00032 #include "internal.h"
00033 #include "video.h"
00034 #include "libavutil/eval.h"
00035 #include "libavutil/avstring.h"
00036 #include "libavutil/internal.h"
00037 #include "libavutil/libm.h"
00038 #include "libavutil/imgutils.h"
00039 #include "libavutil/mathematics.h"
00040 #include "libavutil/opt.h"
00041
00042 static const char *const var_names[] = {
00043 "in_w", "iw",
00044 "in_h", "ih",
00045 "out_w", "ow",
00046 "out_h", "oh",
00047 "a",
00048 "sar",
00049 "dar",
00050 "hsub",
00051 "vsub",
00052 "x",
00053 "y",
00054 "n",
00055 "pos",
00056 "t",
00057 NULL
00058 };
00059
00060 enum var_name {
00061 VAR_IN_W, VAR_IW,
00062 VAR_IN_H, VAR_IH,
00063 VAR_OUT_W, VAR_OW,
00064 VAR_OUT_H, VAR_OH,
00065 VAR_A,
00066 VAR_SAR,
00067 VAR_DAR,
00068 VAR_HSUB,
00069 VAR_VSUB,
00070 VAR_X,
00071 VAR_Y,
00072 VAR_N,
00073 VAR_POS,
00074 VAR_T,
00075 VAR_VARS_NB
00076 };
00077
00078 typedef struct {
00079 const AVClass *class;
00080 int x;
00081 int y;
00082 int w;
00083 int h;
00084
00085 AVRational out_sar;
00086 int keep_aspect;
00087
00088 int max_step[4];
00089 int hsub, vsub;
00090 char *x_expr, *y_expr, *w_expr, *h_expr;
00091 AVExpr *x_pexpr, *y_pexpr;
00092 double var_values[VAR_VARS_NB];
00093 } CropContext;
00094
00095 #define OFFSET(x) offsetof(CropContext, x)
00096 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
00097
00098 static const AVOption crop_options[] = {
00099 { "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 },
00100 { "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 },
00101 { "out_w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
00102 { "w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
00103 { "out_h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
00104 { "h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
00105 { "keep_aspect", "force packed RGB in input and output", OFFSET(keep_aspect), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
00106 {NULL}
00107 };
00108
00109 AVFILTER_DEFINE_CLASS(crop);
00110
00111 static av_cold int init(AVFilterContext *ctx, const char *args)
00112 {
00113 CropContext *crop = ctx->priv;
00114 static const char *shorthand[] = { "w", "h", "x", "y", "keep_aspect", NULL };
00115
00116 crop->class = &crop_class;
00117 av_opt_set_defaults(crop);
00118
00119 return av_opt_set_from_string(crop, args, shorthand, "=", ":");
00120 }
00121
00122 static av_cold void uninit(AVFilterContext *ctx)
00123 {
00124 CropContext *crop = ctx->priv;
00125
00126 av_expr_free(crop->x_pexpr); crop->x_pexpr = NULL;
00127 av_expr_free(crop->y_pexpr); crop->y_pexpr = NULL;
00128 av_opt_free(crop);
00129 }
00130
00131 static int query_formats(AVFilterContext *ctx)
00132 {
00133 static const enum AVPixelFormat pix_fmts[] = {
00134 AV_PIX_FMT_RGB48BE, AV_PIX_FMT_RGB48LE,
00135 AV_PIX_FMT_BGR48BE, AV_PIX_FMT_BGR48LE,
00136 AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA,
00137 AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA,
00138 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
00139 AV_PIX_FMT_RGB565BE, AV_PIX_FMT_RGB565LE,
00140 AV_PIX_FMT_RGB555BE, AV_PIX_FMT_RGB555LE,
00141 AV_PIX_FMT_BGR565BE, AV_PIX_FMT_BGR565LE,
00142 AV_PIX_FMT_BGR555BE, AV_PIX_FMT_BGR555LE,
00143 AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY16LE,
00144 AV_PIX_FMT_YUV420P16LE, AV_PIX_FMT_YUV420P16BE,
00145 AV_PIX_FMT_YUV422P16LE, AV_PIX_FMT_YUV422P16BE,
00146 AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUV444P16BE,
00147 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P,
00148 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
00149 AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P,
00150 AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P,
00151 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ440P,
00152 AV_PIX_FMT_YUVA420P,
00153 AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8,
00154 AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE,
00155 AV_PIX_FMT_PAL8, AV_PIX_FMT_GRAY8,
00156 AV_PIX_FMT_NONE
00157 };
00158
00159 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00160
00161 return 0;
00162 }
00163
00164 static inline int normalize_double(int *n, double d)
00165 {
00166 int ret = 0;
00167
00168 if (isnan(d)) {
00169 ret = AVERROR(EINVAL);
00170 } else if (d > INT_MAX || d < INT_MIN) {
00171 *n = d > INT_MAX ? INT_MAX : INT_MIN;
00172 ret = AVERROR(EINVAL);
00173 } else
00174 *n = round(d);
00175
00176 return ret;
00177 }
00178
00179 static int config_input(AVFilterLink *link)
00180 {
00181 AVFilterContext *ctx = link->dst;
00182 CropContext *crop = ctx->priv;
00183 const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(link->format);
00184 int ret;
00185 const char *expr;
00186 double res;
00187
00188 crop->var_values[VAR_IN_W] = crop->var_values[VAR_IW] = ctx->inputs[0]->w;
00189 crop->var_values[VAR_IN_H] = crop->var_values[VAR_IH] = ctx->inputs[0]->h;
00190 crop->var_values[VAR_A] = (float) link->w / link->h;
00191 crop->var_values[VAR_SAR] = link->sample_aspect_ratio.num ? av_q2d(link->sample_aspect_ratio) : 1;
00192 crop->var_values[VAR_DAR] = crop->var_values[VAR_A] * crop->var_values[VAR_SAR];
00193 crop->var_values[VAR_HSUB] = 1<<pix_desc->log2_chroma_w;
00194 crop->var_values[VAR_VSUB] = 1<<pix_desc->log2_chroma_h;
00195 crop->var_values[VAR_X] = NAN;
00196 crop->var_values[VAR_Y] = NAN;
00197 crop->var_values[VAR_OUT_W] = crop->var_values[VAR_OW] = NAN;
00198 crop->var_values[VAR_OUT_H] = crop->var_values[VAR_OH] = NAN;
00199 crop->var_values[VAR_N] = 0;
00200 crop->var_values[VAR_T] = NAN;
00201 crop->var_values[VAR_POS] = NAN;
00202
00203 av_image_fill_max_pixsteps(crop->max_step, NULL, pix_desc);
00204 crop->hsub = pix_desc->log2_chroma_w;
00205 crop->vsub = pix_desc->log2_chroma_h;
00206
00207 if ((ret = av_expr_parse_and_eval(&res, (expr = crop->w_expr),
00208 var_names, crop->var_values,
00209 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) goto fail_expr;
00210 crop->var_values[VAR_OUT_W] = crop->var_values[VAR_OW] = res;
00211 if ((ret = av_expr_parse_and_eval(&res, (expr = crop->h_expr),
00212 var_names, crop->var_values,
00213 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) goto fail_expr;
00214 crop->var_values[VAR_OUT_H] = crop->var_values[VAR_OH] = res;
00215
00216 if ((ret = av_expr_parse_and_eval(&res, (expr = crop->w_expr),
00217 var_names, crop->var_values,
00218 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) goto fail_expr;
00219 crop->var_values[VAR_OUT_W] = crop->var_values[VAR_OW] = res;
00220 if (normalize_double(&crop->w, crop->var_values[VAR_OUT_W]) < 0 ||
00221 normalize_double(&crop->h, crop->var_values[VAR_OUT_H]) < 0) {
00222 av_log(ctx, AV_LOG_ERROR,
00223 "Too big value or invalid expression for out_w/ow or out_h/oh. "
00224 "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n",
00225 crop->w_expr, crop->h_expr);
00226 return AVERROR(EINVAL);
00227 }
00228 crop->w &= ~((1 << crop->hsub) - 1);
00229 crop->h &= ~((1 << crop->vsub) - 1);
00230
00231 if ((ret = av_expr_parse(&crop->x_pexpr, crop->x_expr, var_names,
00232 NULL, NULL, NULL, NULL, 0, ctx)) < 0 ||
00233 (ret = av_expr_parse(&crop->y_pexpr, crop->y_expr, var_names,
00234 NULL, NULL, NULL, NULL, 0, ctx)) < 0)
00235 return AVERROR(EINVAL);
00236
00237 if (crop->keep_aspect) {
00238 AVRational dar = av_mul_q(link->sample_aspect_ratio,
00239 (AVRational){ link->w, link->h });
00240 av_reduce(&crop->out_sar.num, &crop->out_sar.den,
00241 dar.num * crop->h, dar.den * crop->w, INT_MAX);
00242 } else
00243 crop->out_sar = link->sample_aspect_ratio;
00244
00245 av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d sar:%d/%d -> w:%d h:%d sar:%d/%d\n",
00246 link->w, link->h, link->sample_aspect_ratio.num, link->sample_aspect_ratio.den,
00247 crop->w, crop->h, crop->out_sar.num, crop->out_sar.den);
00248
00249 if (crop->w <= 0 || crop->h <= 0 ||
00250 crop->w > link->w || crop->h > link->h) {
00251 av_log(ctx, AV_LOG_ERROR,
00252 "Invalid too big or non positive size for width '%d' or height '%d'\n",
00253 crop->w, crop->h);
00254 return AVERROR(EINVAL);
00255 }
00256
00257
00258 crop->x = (link->w - crop->w) / 2;
00259 crop->y = (link->h - crop->h) / 2;
00260 crop->x &= ~((1 << crop->hsub) - 1);
00261 crop->y &= ~((1 << crop->vsub) - 1);
00262 return 0;
00263
00264 fail_expr:
00265 av_log(NULL, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr);
00266 return ret;
00267 }
00268
00269 static int config_output(AVFilterLink *link)
00270 {
00271 CropContext *crop = link->src->priv;
00272
00273 link->w = crop->w;
00274 link->h = crop->h;
00275 link->sample_aspect_ratio = crop->out_sar;
00276
00277 return 0;
00278 }
00279
00280 static int filter_frame(AVFilterLink *link, AVFilterBufferRef *frame)
00281 {
00282 AVFilterContext *ctx = link->dst;
00283 CropContext *crop = ctx->priv;
00284 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
00285 int i;
00286
00287 frame->video->w = crop->w;
00288 frame->video->h = crop->h;
00289
00290 crop->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
00291 NAN : frame->pts * av_q2d(link->time_base);
00292 crop->var_values[VAR_POS] = frame->pos == -1 ? NAN : frame->pos;
00293 crop->var_values[VAR_X] = av_expr_eval(crop->x_pexpr, crop->var_values, NULL);
00294 crop->var_values[VAR_Y] = av_expr_eval(crop->y_pexpr, crop->var_values, NULL);
00295 crop->var_values[VAR_X] = av_expr_eval(crop->x_pexpr, crop->var_values, NULL);
00296
00297 normalize_double(&crop->x, crop->var_values[VAR_X]);
00298 normalize_double(&crop->y, crop->var_values[VAR_Y]);
00299
00300 if (crop->x < 0) crop->x = 0;
00301 if (crop->y < 0) crop->y = 0;
00302 if ((unsigned)crop->x + (unsigned)crop->w > link->w) crop->x = link->w - crop->w;
00303 if ((unsigned)crop->y + (unsigned)crop->h > link->h) crop->y = link->h - crop->h;
00304 crop->x &= ~((1 << crop->hsub) - 1);
00305 crop->y &= ~((1 << crop->vsub) - 1);
00306
00307 av_dlog(ctx, "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n",
00308 (int)crop->var_values[VAR_N], crop->var_values[VAR_T], crop->x,
00309 crop->y, crop->x+crop->w, crop->y+crop->h);
00310
00311 frame->data[0] += crop->y * frame->linesize[0];
00312 frame->data[0] += crop->x * crop->max_step[0];
00313
00314 if (!(desc->flags & PIX_FMT_PAL || desc->flags & PIX_FMT_PSEUDOPAL)) {
00315 for (i = 1; i < 3; i ++) {
00316 if (frame->data[i]) {
00317 frame->data[i] += (crop->y >> crop->vsub) * frame->linesize[i];
00318 frame->data[i] += (crop->x * crop->max_step[i]) >> crop->hsub;
00319 }
00320 }
00321 }
00322
00323
00324 if (frame->data[3]) {
00325 frame->data[3] += crop->y * frame->linesize[3];
00326 frame->data[3] += crop->x * crop->max_step[3];
00327 }
00328
00329 crop->var_values[VAR_N] += 1.0;
00330
00331 return ff_filter_frame(link->dst->outputs[0], frame);
00332 }
00333
00334 static const AVFilterPad avfilter_vf_crop_inputs[] = {
00335 {
00336 .name = "default",
00337 .type = AVMEDIA_TYPE_VIDEO,
00338 .filter_frame = filter_frame,
00339 .get_video_buffer = ff_null_get_video_buffer,
00340 .config_props = config_input,
00341 },
00342 { NULL }
00343 };
00344
00345 static const AVFilterPad avfilter_vf_crop_outputs[] = {
00346 {
00347 .name = "default",
00348 .type = AVMEDIA_TYPE_VIDEO,
00349 .config_props = config_output,
00350 },
00351 { NULL }
00352 };
00353
00354 AVFilter avfilter_vf_crop = {
00355 .name = "crop",
00356 .description = NULL_IF_CONFIG_SMALL("Crop the input video to width:height:x:y."),
00357
00358 .priv_size = sizeof(CropContext),
00359
00360 .query_formats = query_formats,
00361 .init = init,
00362 .uninit = uninit,
00363
00364 .inputs = avfilter_vf_crop_inputs,
00365 .outputs = avfilter_vf_crop_outputs,
00366 .priv_class = &crop_class,
00367 };