diff --git a/doc/filters.texi b/doc/filters.texi index e081cdc7bc..01262d845e 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -15369,6 +15369,18 @@ Please note that this is a different thing than specifying -1 for @option{w} or @option{h}, you still need to specify the output resolution for this option to work. +@item force_divisible_by Ensures that the output resolution is divisible by the +given integer when used together with @option{force_original_aspect_ratio}. This +works similar to using -n in the @option{w} and @option{h} options. + +This option respects the value set for @option{force_original_aspect_ratio}, +increasing or decreasing the resolution accordingly. This may slightly modify +the video's aspect ration. + +This can be handy, for example, if you want to have a video fit within a defined +resolution using the @option{force_original_aspect_ratio} option but have +encoder restrictions when it comes to width or height. + @end table The values of the @option{w} and @option{h} options are expressions diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c index 7aebf56ad8..bf340b8e7b 100644 --- a/libavfilter/vf_scale.c +++ b/libavfilter/vf_scale.c @@ -86,6 +86,7 @@ typedef struct ScaleContext { int in_v_chr_pos; int force_original_aspect_ratio; + int force_divisible_by; int nb_slices; @@ -237,7 +238,8 @@ static int config_props(AVFilterLink *outlink) goto fail; /* Note that force_original_aspect_ratio may overwrite the previous set - * dimensions so that it is not divisible by the set factors anymore. */ + * dimensions so that it is not divisible by the set factors anymore + * unless force_divisible_by is defined as well */ if (scale->force_original_aspect_ratio) { int tmp_w = av_rescale(h, inlink->w, inlink->h); int tmp_h = av_rescale(w, inlink->h, inlink->w); @@ -245,9 +247,19 @@ static int config_props(AVFilterLink *outlink) if (scale->force_original_aspect_ratio == 1) { w = FFMIN(tmp_w, w); h = FFMIN(tmp_h, h); + if (scale->force_divisible_by > 1) { + // round down + w = w / scale->force_divisible_by * scale->force_divisible_by; + h = h / scale->force_divisible_by * scale->force_divisible_by; + } } else { w = FFMAX(tmp_w, w); h = FFMAX(tmp_h, h); + if (scale->force_divisible_by > 1) { + // round up + w = (w + scale->force_divisible_by - 1) / scale->force_divisible_by * scale->force_divisible_by; + h = (h + scale->force_divisible_by - 1) / scale->force_divisible_by * scale->force_divisible_by; + } } } @@ -600,6 +612,7 @@ static const AVOption scale_options[] = { { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, + { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1}, 1, 256, FLAGS }, { "param0", "Scaler param 0", OFFSET(param[0]), AV_OPT_TYPE_DOUBLE, { .dbl = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, FLAGS }, { "param1", "Scaler param 1", OFFSET(param[1]), AV_OPT_TYPE_DOUBLE, { .dbl = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, FLAGS }, { "nb_slices", "set the number of slices (debug purpose only)", OFFSET(nb_slices), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },