1/* 2 * Copyright (c) 2010 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 * aspect ratio modification video filters 24 */ 25 26#include <float.h> 27 28#include "libavutil/common.h" 29#include "libavutil/eval.h" 30#include "libavutil/mathematics.h" 31#include "libavutil/opt.h" 32#include "libavutil/parseutils.h" 33#include "libavutil/pixdesc.h" 34 35#include "avfilter.h" 36#include "internal.h" 37#include "video.h" 38 39static const char *const var_names[] = { 40 "w", 41 "h", 42 "a", "dar", 43 "sar", 44 "hsub", 45 "vsub", 46 NULL 47}; 48 49enum var_name { 50 VAR_W, 51 VAR_H, 52 VAR_A, VAR_DAR, 53 VAR_SAR, 54 VAR_HSUB, 55 VAR_VSUB, 56 VARS_NB 57}; 58 59typedef struct AspectContext { 60 const AVClass *class; 61 AVRational dar; 62 AVRational sar; 63 int max; 64#if FF_API_OLD_FILTER_OPTS 65 float aspect_den; 66#endif 67 char *ratio_expr; 68} AspectContext; 69 70static av_cold int init(AVFilterContext *ctx) 71{ 72 AspectContext *s = ctx->priv; 73 int ret; 74 75#if FF_API_OLD_FILTER_OPTS 76 if (s->ratio_expr && s->aspect_den > 0) { 77 double num; 78 av_log(ctx, AV_LOG_WARNING, 79 "num:den syntax is deprecated, please use num/den or named options instead\n"); 80 ret = av_expr_parse_and_eval(&num, s->ratio_expr, NULL, NULL, 81 NULL, NULL, NULL, NULL, NULL, 0, ctx); 82 if (ret < 0) { 83 av_log(ctx, AV_LOG_ERROR, "Unable to parse ratio numerator \"%s\"\n", s->ratio_expr); 84 return AVERROR(EINVAL); 85 } 86 s->sar = s->dar = av_d2q(num / s->aspect_den, s->max); 87 } 88#endif 89 90 return 0; 91} 92 93static int filter_frame(AVFilterLink *link, AVFrame *frame) 94{ 95 AspectContext *s = link->dst->priv; 96 97 frame->sample_aspect_ratio = s->sar; 98 return ff_filter_frame(link->dst->outputs[0], frame); 99} 100 101#define OFFSET(x) offsetof(AspectContext, x) 102#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM 103 104static inline void compute_dar(AVRational *dar, AVRational sar, int w, int h) 105{ 106 if (sar.num && sar.den) { 107 av_reduce(&dar->num, &dar->den, sar.num * w, sar.den * h, INT_MAX); 108 } else { 109 av_reduce(&dar->num, &dar->den, w, h, INT_MAX); 110 } 111} 112 113static int get_aspect_ratio(AVFilterLink *inlink, AVRational *aspect_ratio) 114{ 115 AVFilterContext *ctx = inlink->dst; 116 AspectContext *s = inlink->dst->priv; 117 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); 118 double var_values[VARS_NB], res; 119 int ret; 120 121 var_values[VAR_W] = inlink->w; 122 var_values[VAR_H] = inlink->h; 123 var_values[VAR_A] = (double) inlink->w / inlink->h; 124 var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? 125 (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1; 126 var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; 127 var_values[VAR_HSUB] = 1 << desc->log2_chroma_w; 128 var_values[VAR_VSUB] = 1 << desc->log2_chroma_h; 129 130 /* evaluate new aspect ratio*/ 131 ret = av_expr_parse_and_eval(&res, s->ratio_expr, 132 var_names, var_values, 133 NULL, NULL, NULL, NULL, NULL, 0, ctx); 134 if (ret < 0) { 135 ret = av_parse_ratio(aspect_ratio, s->ratio_expr, s->max, 0, ctx); 136 } else 137 *aspect_ratio = av_d2q(res, s->max); 138 139 if (ret < 0) { 140 av_log(ctx, AV_LOG_ERROR, 141 "Error when evaluating the expression '%s'\n", s->ratio_expr); 142 return ret; 143 } 144 if (aspect_ratio->num < 0 || aspect_ratio->den <= 0) { 145 av_log(ctx, AV_LOG_ERROR, 146 "Invalid string '%s' for aspect ratio\n", s->ratio_expr); 147 return AVERROR(EINVAL); 148 } 149 return 0; 150} 151 152#if CONFIG_SETDAR_FILTER 153 154static int setdar_config_props(AVFilterLink *inlink) 155{ 156 AspectContext *s = inlink->dst->priv; 157 AVRational dar; 158 AVRational old_dar; 159 AVRational old_sar = inlink->sample_aspect_ratio; 160 int ret; 161 162#if FF_API_OLD_FILTER_OPTS 163 if (!(s->ratio_expr && s->aspect_den > 0)) { 164#endif 165 if ((ret = get_aspect_ratio(inlink, &s->dar))) 166 return ret; 167#if FF_API_OLD_FILTER_OPTS 168 } 169#endif 170 171 if (s->dar.num && s->dar.den) { 172 av_reduce(&s->sar.num, &s->sar.den, 173 s->dar.num * inlink->h, 174 s->dar.den * inlink->w, INT_MAX); 175 inlink->sample_aspect_ratio = s->sar; 176 dar = s->dar; 177 } else { 178 inlink->sample_aspect_ratio = (AVRational){ 1, 1 }; 179 dar = (AVRational){ inlink->w, inlink->h }; 180 } 181 182 compute_dar(&old_dar, old_sar, inlink->w, inlink->h); 183 av_log(inlink->dst, AV_LOG_VERBOSE, "w:%d h:%d dar:%d/%d sar:%d/%d -> dar:%d/%d sar:%d/%d\n", 184 inlink->w, inlink->h, old_dar.num, old_dar.den, old_sar.num, old_sar.den, 185 dar.num, dar.den, inlink->sample_aspect_ratio.num, inlink->sample_aspect_ratio.den); 186 187 return 0; 188} 189 190static const AVOption setdar_options[] = { 191 { "dar", "set display aspect ratio", OFFSET(ratio_expr), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, 192 { "ratio", "set display aspect ratio", OFFSET(ratio_expr), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, 193 { "r", "set display aspect ratio", OFFSET(ratio_expr), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, 194#if FF_API_OLD_FILTER_OPTS 195 { "dar_den", NULL, OFFSET(aspect_den), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, 0, FLT_MAX, FLAGS }, 196#endif 197 { "max", "set max value for nominator or denominator in the ratio", OFFSET(max), AV_OPT_TYPE_INT, {.i64=100}, 1, INT_MAX, FLAGS }, 198 { NULL } 199}; 200 201AVFILTER_DEFINE_CLASS(setdar); 202 203static const AVFilterPad avfilter_vf_setdar_inputs[] = { 204 { 205 .name = "default", 206 .type = AVMEDIA_TYPE_VIDEO, 207 .config_props = setdar_config_props, 208 .filter_frame = filter_frame, 209 }, 210 { NULL } 211}; 212 213static const AVFilterPad avfilter_vf_setdar_outputs[] = { 214 { 215 .name = "default", 216 .type = AVMEDIA_TYPE_VIDEO, 217 }, 218 { NULL } 219}; 220 221AVFilter ff_vf_setdar = { 222 .name = "setdar", 223 .description = NULL_IF_CONFIG_SMALL("Set the frame display aspect ratio."), 224 .init = init, 225 .priv_size = sizeof(AspectContext), 226 .priv_class = &setdar_class, 227 .inputs = avfilter_vf_setdar_inputs, 228 .outputs = avfilter_vf_setdar_outputs, 229}; 230 231#endif /* CONFIG_SETDAR_FILTER */ 232 233#if CONFIG_SETSAR_FILTER 234 235static int setsar_config_props(AVFilterLink *inlink) 236{ 237 AspectContext *s = inlink->dst->priv; 238 AVRational old_sar = inlink->sample_aspect_ratio; 239 AVRational old_dar, dar; 240 int ret; 241 242#if FF_API_OLD_FILTER_OPTS 243 if (!(s->ratio_expr && s->aspect_den > 0)) { 244#endif 245 if ((ret = get_aspect_ratio(inlink, &s->sar))) 246 return ret; 247#if FF_API_OLD_FILTER_OPTS 248 } 249#endif 250 251 inlink->sample_aspect_ratio = s->sar; 252 253 compute_dar(&old_dar, old_sar, inlink->w, inlink->h); 254 compute_dar(&dar, s->sar, inlink->w, inlink->h); 255 av_log(inlink->dst, AV_LOG_VERBOSE, "w:%d h:%d sar:%d/%d dar:%d/%d -> sar:%d/%d dar:%d/%d\n", 256 inlink->w, inlink->h, old_sar.num, old_sar.den, old_dar.num, old_dar.den, 257 inlink->sample_aspect_ratio.num, inlink->sample_aspect_ratio.den, dar.num, dar.den); 258 259 return 0; 260} 261 262static const AVOption setsar_options[] = { 263 { "sar", "set sample (pixel) aspect ratio", OFFSET(ratio_expr), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, 264 { "ratio", "set sample (pixel) aspect ratio", OFFSET(ratio_expr), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, 265 { "r", "set sample (pixel) aspect ratio", OFFSET(ratio_expr), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, 266#if FF_API_OLD_FILTER_OPTS 267 { "sar_den", NULL, OFFSET(aspect_den), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, 0, FLT_MAX, FLAGS }, 268#endif 269 { "max", "set max value for nominator or denominator in the ratio", OFFSET(max), AV_OPT_TYPE_INT, {.i64=100}, 1, INT_MAX, FLAGS }, 270 { NULL } 271}; 272 273AVFILTER_DEFINE_CLASS(setsar); 274 275static const AVFilterPad avfilter_vf_setsar_inputs[] = { 276 { 277 .name = "default", 278 .type = AVMEDIA_TYPE_VIDEO, 279 .config_props = setsar_config_props, 280 .filter_frame = filter_frame, 281 }, 282 { NULL } 283}; 284 285static const AVFilterPad avfilter_vf_setsar_outputs[] = { 286 { 287 .name = "default", 288 .type = AVMEDIA_TYPE_VIDEO, 289 }, 290 { NULL } 291}; 292 293AVFilter ff_vf_setsar = { 294 .name = "setsar", 295 .description = NULL_IF_CONFIG_SMALL("Set the pixel sample aspect ratio."), 296 .init = init, 297 .priv_size = sizeof(AspectContext), 298 .priv_class = &setsar_class, 299 .inputs = avfilter_vf_setsar_inputs, 300 .outputs = avfilter_vf_setsar_outputs, 301}; 302 303#endif /* CONFIG_SETSAR_FILTER */ 304