Skip to content

Commit a28dc06

Browse files
committed
avfilter/scale*: add option reset_sar
For anamorphic videos, enabling this option leads to adjustment of output dimensions to obtain square pixels when the user requests proportional scaling through either of the w/h expressions or force_original_aspect_ratio. Output SAR is always reset to 1. Option added to scale, scale_cuda, scale_npp & scale_vaapi. libplacebo already has a similar option with different semantics, scale_vt and scale_vulkan don't implement force_oar, so for these three filters, I've made minimal changes needed to not break building or change output.
1 parent e838e6c commit a28dc06

File tree

10 files changed

+100
-24
lines changed

10 files changed

+100
-24
lines changed

doc/filters.texi

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21285,6 +21285,14 @@ This option can be handy if you need to have a video fit within or exceed
2128521285
a defined resolution using @option{force_original_aspect_ratio} but also have
2128621286
encoder restrictions on width or height divisibility.
2128721287

21288+
@item reset_sar
21289+
Enabling this option leads to the output SAR being reset to 1.
21290+
Additionally, if the user requests proportional scaling either
21291+
through the width or height expressions, e.g. @code{w=-4:h=360} or @code{w=iw/2:h=-1}
21292+
or by enabling @code{force_original_aspect_ratio}, then the input DAR is taken into
21293+
account and the output is scaled to produce square pixels.
21294+
Default is false.
21295+
2128821296
@end table
2128921297

2129021298
The values of the @option{w} and @option{h} options are expressions
@@ -21445,10 +21453,28 @@ scale='trunc(ih*dar):ih',setsar=1/1
2144521453
@end example
2144621454

2144721455
@item
21448-
Make pixels square by combining scale and setsar,
21456+
Make pixels square using reset_sar,
2144921457
making sure the resulting resolution is even (required by some codecs):
2145021458
@example
21451-
scale='trunc(ih*dar/2)*2:trunc(ih/2)*2',setsar=1/1
21459+
scale='-2:ih-mod(ih,2):reset_sar=1'
21460+
@end example
21461+
21462+
@item
21463+
Scale to target exactly, however reset SAR to 1:
21464+
@example
21465+
scale='400:300:reset_sar=1'
21466+
@end example
21467+
21468+
@item
21469+
Scale to even dimensions that fit within 400x300, preserving input SAR:
21470+
@example
21471+
scale='400:300:force_original_aspect_ratio=decrease:force_divisible_by=2'
21472+
@end example
21473+
21474+
@item
21475+
Scale to produce square pixels with even dimensions that fit within 400x300:
21476+
@example
21477+
scale='400:300:force_original_aspect_ratio=decrease:force_divisible_by=2:reset_sar=1'
2145221478
@end example
2145321479

2145421480
@item
@@ -21538,6 +21564,9 @@ Affects the curves of the bicubic algorithm.
2153821564
@item force_divisible_by
2153921565
Work the same as the identical @ref{scale} filter options.
2154021566

21567+
@item reset_sar
21568+
Works the same as the identical @ref{scale} filter option.
21569+
2154121570
@end table
2154221571

2154321572
@subsection Examples
@@ -21641,6 +21670,9 @@ This option can be handy if you need to have a video fit within or exceed
2164121670
a defined resolution using @option{force_original_aspect_ratio} but also have
2164221671
encoder restrictions on width or height divisibility.
2164321672

21673+
@item reset_sar
21674+
Works the same as the identical @ref{scale} filter option.
21675+
2164421676
@item eval
2164521677
Specify when to evaluate @var{width} and @var{height} expression. It accepts the following values:
2164621678

libavfilter/scale_eval.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ int ff_scale_eval_dimensions(void *log_ctx,
112112

113113
int ff_scale_adjust_dimensions(AVFilterLink *inlink,
114114
int *ret_w, int *ret_h,
115-
int force_original_aspect_ratio, int force_divisible_by)
115+
int force_original_aspect_ratio, int force_divisible_by,
116+
double w_adj)
116117
{
117118
int64_t w, h;
118119
int factor_w, factor_h;
@@ -132,26 +133,26 @@ int ff_scale_adjust_dimensions(AVFilterLink *inlink,
132133
}
133134

134135
if (w < 0 && h < 0) {
135-
w = inlink->w;
136+
w = inlink->w * w_adj;
136137
h = inlink->h;
137138
}
138139

139140
/* Make sure that the result is divisible by the factor we determined
140141
* earlier. If no factor was set, nothing will happen as the default
141142
* factor is 1 */
142143
if (w < 0)
143-
w = av_rescale(h, inlink->w, inlink->h * factor_w) * factor_w;
144+
w = av_rescale(h, inlink->w * w_adj, inlink->h * factor_w) * factor_w;
144145
if (h < 0)
145-
h = av_rescale(w, inlink->h, inlink->w * factor_h) * factor_h;
146+
h = av_rescale(w, inlink->h, inlink->w * w_adj * factor_h) * factor_h;
146147

147148
/* Note that force_original_aspect_ratio may overwrite the previous set
148149
* dimensions so that it is not divisible by the set factors anymore
149150
* unless force_divisible_by is defined as well */
150151
if (force_original_aspect_ratio) {
151152
// Including force_divisible_by here rounds to the nearest multiple of it.
152-
int64_t tmp_w = av_rescale(h, inlink->w, inlink->h * (int64_t)force_divisible_by)
153+
int64_t tmp_w = av_rescale(h, inlink->w * w_adj, inlink->h * (int64_t)force_divisible_by)
153154
* force_divisible_by;
154-
int64_t tmp_h = av_rescale(w, inlink->h, inlink->w * (int64_t)force_divisible_by)
155+
int64_t tmp_h = av_rescale(w, inlink->h, inlink->w * w_adj * (int64_t)force_divisible_by)
155156
* force_divisible_by;
156157

157158
if (force_original_aspect_ratio == 1) {

libavfilter/scale_eval.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,13 @@ int ff_scale_eval_dimensions(void *ctx,
4040
* or both of the evaluated values are of the form '-n' or if
4141
* force_original_aspect_ratio is set. force_divisible_by is used only when
4242
* force_original_aspect_ratio is set and must be at least 1.
43+
* w_adj is the input SAR when the output dimensions are intended to be square
44+
* pixels, else should be 1.
4345
*
4446
* Returns negative error code on error or non negative on success
4547
*/
4648
int ff_scale_adjust_dimensions(AVFilterLink *inlink,
4749
int *ret_w, int *ret_h,
48-
int force_original_aspect_ratio, int force_divisible_by);
50+
int force_original_aspect_ratio, int force_divisible_by,
51+
double w_adj);
4952
#endif

libavfilter/vf_libplacebo.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,7 @@ static int libplacebo_config_output(AVFilterLink *outlink)
12011201

12021202
ff_scale_adjust_dimensions(inlink, &outlink->w, &outlink->h,
12031203
s->force_original_aspect_ratio,
1204-
s->force_divisible_by);
1204+
s->force_divisible_by, 1.f);
12051205

12061206
if (s->normalize_sar || s->nb_inputs > 1) {
12071207
/* SAR is normalized, or we have multiple inputs, set out to 1:1 */

libavfilter/vf_scale.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ typedef struct ScaleContext {
176176

177177
int force_original_aspect_ratio;
178178
int force_divisible_by;
179+
int reset_sar;
179180

180181
int eval_mode; ///< expression evaluation mode
181182

@@ -645,6 +646,7 @@ static int config_props(AVFilterLink *outlink)
645646
outlink->src->inputs[0];
646647
ScaleContext *scale = ctx->priv;
647648
uint8_t *flags_val = NULL;
649+
double w_adj = 1.0;
648650
int ret;
649651

650652
if ((ret = scale_eval_dimensions(ctx)) < 0)
@@ -653,9 +655,13 @@ static int config_props(AVFilterLink *outlink)
653655
outlink->w = scale->w;
654656
outlink->h = scale->h;
655657

658+
if (scale->reset_sar)
659+
w_adj = IS_SCALE2REF(ctx) ? scale->var_values[VAR_S2R_MAIN_SAR] :
660+
scale->var_values[VAR_SAR];
661+
656662
ret = ff_scale_adjust_dimensions(inlink, &outlink->w, &outlink->h,
657663
scale->force_original_aspect_ratio,
658-
scale->force_divisible_by);
664+
scale->force_divisible_by, w_adj);
659665

660666
if (ret < 0)
661667
goto fail;
@@ -668,7 +674,9 @@ static int config_props(AVFilterLink *outlink)
668674

669675
/* TODO: make algorithm configurable */
670676

671-
if (inlink0->sample_aspect_ratio.num){
677+
if (scale->reset_sar)
678+
outlink->sample_aspect_ratio = (AVRational){1, 1};
679+
else if (inlink0->sample_aspect_ratio.num){
672680
outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink0->w, outlink->w * inlink0->h}, inlink0->sample_aspect_ratio);
673681
} else
674682
outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
@@ -1185,6 +1193,7 @@ static const AVOption scale_options[] = {
11851193
{ "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" },
11861194
{ "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" },
11871195
{ "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 },
1196+
{ "reset_sar", "reset SAR to 1 and scale to square pixels if scaling proportionally", OFFSET(reset_sar), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
11881197
{ "param0", "Scaler param 0", OFFSET(param[0]), AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS },
11891198
{ "param1", "Scaler param 1", OFFSET(param[1]), AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS },
11901199
{ "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" },

libavfilter/vf_scale_cuda.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ typedef struct CUDAScaleContext {
9696

9797
int force_original_aspect_ratio;
9898
int force_divisible_by;
99+
int reset_sar;
99100

100101
CUcontext cu_ctx;
101102
CUmodule cu_module;
@@ -355,6 +356,7 @@ static av_cold int cudascale_config_props(AVFilterLink *outlink)
355356
AVHWFramesContext *frames_ctx;
356357
AVCUDADeviceContext *device_hwctx;
357358
int w, h;
359+
double w_adj = 1.0;
358360
int ret;
359361

360362
if ((ret = ff_scale_eval_dimensions(s,
@@ -363,8 +365,12 @@ static av_cold int cudascale_config_props(AVFilterLink *outlink)
363365
&w, &h)) < 0)
364366
goto fail;
365367

368+
if (s->reset_sar)
369+
w_adj = inlink->sample_aspect_ratio.num ?
370+
(double)inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
371+
366372
ff_scale_adjust_dimensions(inlink, &w, &h,
367-
s->force_original_aspect_ratio, s->force_divisible_by);
373+
s->force_original_aspect_ratio, s->force_divisible_by, w_adj);
368374

369375
if (((int64_t)h * inlink->w) > INT_MAX ||
370376
((int64_t)w * inlink->h) > INT_MAX)
@@ -383,7 +389,9 @@ static av_cold int cudascale_config_props(AVFilterLink *outlink)
383389
s->hwctx = device_hwctx;
384390
s->cu_stream = s->hwctx->stream;
385391

386-
if (inlink->sample_aspect_ratio.num) {
392+
if (s->reset_sar)
393+
outlink->sample_aspect_ratio = (AVRational){1, 1};
394+
else if (inlink->sample_aspect_ratio.num) {
387395
outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h*inlink->w,
388396
outlink->w*inlink->h},
389397
inlink->sample_aspect_ratio);
@@ -574,10 +582,14 @@ static int cudascale_filter_frame(AVFilterLink *link, AVFrame *in)
574582
if (ret < 0)
575583
goto fail;
576584

577-
av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den,
578-
(int64_t)in->sample_aspect_ratio.num * outlink->h * link->w,
579-
(int64_t)in->sample_aspect_ratio.den * outlink->w * link->h,
580-
INT_MAX);
585+
if (s->reset_sar) {
586+
out->sample_aspect_ratio = (AVRational){1, 1};
587+
} else {
588+
av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den,
589+
(int64_t)in->sample_aspect_ratio.num * outlink->h * link->w,
590+
(int64_t)in->sample_aspect_ratio.den * outlink->w * link->h,
591+
INT_MAX);
592+
}
581593

582594
av_frame_free(&in);
583595
return ff_filter_frame(outlink, out);
@@ -614,6 +626,7 @@ static const AVOption options[] = {
614626
{ "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" },
615627
{ "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" },
616628
{ "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 },
629+
{ "reset_sar", "reset SAR to 1 and scale to square pixels if scaling proportionally", OFFSET(reset_sar), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
617630
{ NULL },
618631
};
619632

libavfilter/vf_scale_npp.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ typedef struct NPPScaleContext {
160160

161161
int force_original_aspect_ratio;
162162
int force_divisible_by;
163+
int reset_sar;
163164

164165
int interp_algo;
165166

@@ -650,14 +651,19 @@ static int config_props(AVFilterLink *outlink)
650651
outlink->src->inputs[1] :
651652
outlink->src->inputs[0];
652653
NPPScaleContext *s = ctx->priv;
654+
double w_adj = 1.0;
653655
int ret;
654656

655657
if ((ret = nppscale_eval_dimensions(ctx)) < 0)
656658
goto fail;
657659

660+
if (s->reset_sar)
661+
w_adj = IS_SCALE2REF(ctx) ? s->var_values[VAR_S2R_MAIN_SAR] :
662+
s->var_values[VAR_SAR];
663+
658664
ff_scale_adjust_dimensions(inlink, &s->w, &s->h,
659665
s->force_original_aspect_ratio,
660-
s->force_divisible_by);
666+
s->force_divisible_by, w_adj);
661667

662668
if (s->w > INT_MAX || s->h > INT_MAX ||
663669
(s->h * inlink->w) > INT_MAX ||
@@ -674,7 +680,9 @@ static int config_props(AVFilterLink *outlink)
674680
av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d -> w:%d h:%d\n",
675681
inlink->w, inlink->h, outlink->w, outlink->h);
676682

677-
if (inlink->sample_aspect_ratio.num)
683+
if (s->reset_sar)
684+
outlink->sample_aspect_ratio = (AVRational){1, 1};
685+
else if (inlink->sample_aspect_ratio.num)
678686
outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h*inlink->w,
679687
outlink->w*inlink->h},
680688
inlink->sample_aspect_ratio);
@@ -1019,6 +1027,7 @@ static const AVOption options[] = {
10191027
{ "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" },
10201028
{ "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" },
10211029
{ "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 },
1030+
{ "reset_sar", "reset SAR to 1 and scale to square pixels if scaling proportionally", OFFSET(reset_sar), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
10221031
{ "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, { .i64 = EVAL_MODE_INIT }, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" },
10231032
{ "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, { .i64 = EVAL_MODE_INIT }, 0, 0, FLAGS, .unit = "eval" },
10241033
{ "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, { .i64 = EVAL_MODE_FRAME }, 0, 0, FLAGS, .unit = "eval" },

libavfilter/vf_scale_vaapi.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ typedef struct ScaleVAAPIContext {
3939

4040
int force_original_aspect_ratio;
4141
int force_divisible_by;
42+
int reset_sar;
4243

4344
char *colour_primaries_string;
4445
char *colour_transfer_string;
@@ -73,6 +74,7 @@ static int scale_vaapi_config_output(AVFilterLink *outlink)
7374
AVFilterContext *avctx = outlink->src;
7475
VAAPIVPPContext *vpp_ctx = avctx->priv;
7576
ScaleVAAPIContext *ctx = avctx->priv;
77+
double w_adj = 1.0;
7678
int err;
7779

7880
if ((err = ff_scale_eval_dimensions(ctx,
@@ -81,8 +83,12 @@ static int scale_vaapi_config_output(AVFilterLink *outlink)
8183
&vpp_ctx->output_width, &vpp_ctx->output_height)) < 0)
8284
return err;
8385

86+
if (ctx->reset_sar)
87+
w_adj = inlink->sample_aspect_ratio.num ?
88+
(double)inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
89+
8490
ff_scale_adjust_dimensions(inlink, &vpp_ctx->output_width, &vpp_ctx->output_height,
85-
ctx->force_original_aspect_ratio, ctx->force_divisible_by);
91+
ctx->force_original_aspect_ratio, ctx->force_divisible_by, w_adj);
8692

8793
if (inlink->w == vpp_ctx->output_width && inlink->h == vpp_ctx->output_height &&
8894
(vpp_ctx->input_frames->sw_format == vpp_ctx->output_format ||
@@ -98,7 +104,9 @@ static int scale_vaapi_config_output(AVFilterLink *outlink)
98104
if (err < 0)
99105
return err;
100106

101-
if (inlink->sample_aspect_ratio.num)
107+
if (ctx->reset_sar)
108+
outlink->sample_aspect_ratio = (AVRational){1, 1};
109+
else if (inlink->sample_aspect_ratio.num)
102110
outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
103111
else
104112
outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
@@ -274,6 +282,7 @@ static const AVOption scale_vaapi_options[] = {
274282
{ "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" },
275283
{ "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" },
276284
{ "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 },
285+
{ "reset_sar", "reset SAR to 1 and scale to square pixels if scaling proportionally", OFFSET(reset_sar), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
277286

278287
{ NULL },
279288
};

libavfilter/vf_scale_vt.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ static int scale_vt_config_output(AVFilterLink *outlink)
248248
if (err < 0)
249249
return err;
250250

251-
ff_scale_adjust_dimensions(inlink, &s->output_width, &s->output_height, 0, 1);
251+
ff_scale_adjust_dimensions(inlink, &s->output_width, &s->output_height, 0, 1, 1.f);
252252

253253
outlink->w = s->output_width;
254254
outlink->h = s->output_height;

libavfilter/vf_scale_vulkan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ static int scale_vulkan_config_output(AVFilterLink *outlink)
336336
if (err < 0)
337337
return err;
338338

339-
ff_scale_adjust_dimensions(inlink, &vkctx->output_width, &vkctx->output_height, 0, 1);
339+
ff_scale_adjust_dimensions(inlink, &vkctx->output_width, &vkctx->output_height, 0, 1, 1.f);
340340

341341
outlink->w = vkctx->output_width;
342342
outlink->h = vkctx->output_height;

0 commit comments

Comments
 (0)