ffmpeg 中av_rescale_rnd 的含义
我看了网上若干帖子,包括分析代码的,感觉都说得不是太清晰,所以自己也来说说.
函数声明:
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
它的作用是计算 "a * b / c" 的值
由于其结果不一定能整除,但其结果是一个整数,所以就牵扯一个取整问题,
这里它定义了五种取整方式
enum AVRounding {
AV_ROUND_ZERO = 0, ///< Round toward zero.
AV_ROUND_INF = 1, ///< Round away from zero.
AV_ROUND_DOWN = 2, ///< Round toward -infinity.
AV_ROUND_UP = 3, ///< Round toward +infinity.
AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero.
AV_ROUND_PASS_MINMAX = 8192, //这个位是个附加位,必需与其它位组合使用,所以算5种模式
};
我们先把英文的翻译成汉文:
AV_ROUND_ZERO ,向0舍入,解释为趋于0,是什么意思?
AV_ROUND_INF ,向无穷舍入,解释为远离0,是什么意思?
AV_ROUND_DOWN, 向下舍入,注释为趋于负无穷
AV_ROUND_UP, 向上舍入,注释为趋于正无穷
AV_ROUND_NEAR_INF 就近无穷, 解释为向最近的方向,1半的化要远离0, 总之就是4舍5入的意思
AV_ROUND_PASS_MINMAX = 8192, //这个位置位时,当a=INT64_MIN或a=INT64_MAX时 a值pass throuth
这么绕, 翻译了也不是太清楚,嗯, 我们从容易理解的地方着手!
我们可以容易理解的是3种:
1. 向上舍入 例如: 123.1 向上舍入为124, 123.9向上舍入为124
2. 向下舍入 例如: 123.1 向下舍入为123, 123.9向下舍入为123
3. 4舍5入, 例如: 123.1 4舍5入为123, 123.9 4舍5入为124, 123.5 舍5入为124
那趋于0舍入,趋于无穷舍入又是什么玩意?
下面看代码, 我没有直接复制,没必要,拣关键的说明问题:
在(libavutil/mathematics.c):58行 (ffmpeg4.4)
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
1. 当有AV_ROUND_PASS_MINMAX 标志时,如果时INT64_MIN,INT64_MAX, 直接返回a
if (rnd & AV_ROUND_PASS_MINMAX) {
if (a == INT64_MIN || a == INT64_MAX)
return a;
}
2. 其它核心运算
r = 0; //默认余数为0,实际包含了模式0(AV_ROUND_ZERO),模式2(AV_ROUND_DOWN)
if (rnd == AV_ROUND_NEAR_INF) //这是模式5(AV_ROUND_NEAR_INF)
r = c / 2; //当4舍5入时,我们取余数为c/2
else if (rnd & 1) //这里包含了模式1(AV_ROUND_INF),模式3(AV_ROUND_UP)
r = c - 1; //当趋于无穷或者UP时,取余数c-1
return (a * b + r) / c; // 计算(a*b+余数)/c
由此我们看到,其实不是五种舍入方式,还是3种舍入方式.
所谓的趋于0舍入就是下舍入。
所谓的趋于无穷舍入就是上舍入。
搞得这么玄幻,何必呢? 可能是历史原因吧.
在FFmpeg中, 一般不直接调用av_rescale_rnd, 而是使用 av_rescale_q_rnd
它的意义是以bq为时基表示的数值a, 表达成以cq为时基的数值, 计算方式为: a*bq/cq
它的声明及代码如下:
int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq, enum AVRounding rnd)
{
int64_t b = bq.num * (int64_t)cq.den;
int64_t c = cq.num * (int64_t)bq.den;
return av_rescale_rnd(a, b, c, rnd);
}
例如:
常见的视频帧时长3600 是以 90K为时基(1,90000)表示成1M时基(1,1000000)
3600*(1/90000)/(1/1000000)=3600*100/9=40000 (us) = 40(ms) 等于 25帧/s
用函数表示:
av_rescale_rnd(duration,base_90k,base_us);