FFmpeg API 之 Timestamp和TimeBase的辅助计算函数

首先,我们查看一下后面要用到的枚举变量AVRounding。

它定义了舍入法,即如何将含有小数部分的数字转化为整数的方法。

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,
};
  • AV_ROUND_ZERO

    朝向零点舍入

  • AV_ROUND_INF

    背向零点方向舍入

  • AV_ROUND_DOWN

    朝向负轴方向舍入

  • AV_ROUND_UP

    朝向正轴方向舍入

  • AV_ROUND_NEAR_INF

    四舍五入法,舍入到离当前最近的整数

  • AV_ROUND_PASS_MINMAX

    这个Flag并不表示一个舍入法,而是针对 INT64_MIN 和 INT64_MIN 这两个特殊值设立的,表示当针对这两个特殊值做舍入时,直接返回这两个特殊值即可,不做舍入。

    AV_NOPTS_VALUE 等同于 INT64_MIN

    以上5个Flag是互斥的,同时只能使用一个,而想要使用这个Flag时,要使用 or 位运算符来组合上述Flag使用。

    示例代码:

	av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);             
   	// Rescaling 3:                              
	//     Calculating 3 * 1 / 2                         
	//     3 / 2 is rounded up to 2                     
	//     => 2                                                        
	av_rescale_rnd(AV_NOPTS_VALUE, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
	// Rescaling AV_NOPTS_VALUE:                         
	//     AV_NOPTS_VALUE == INT64_MIN                   
	//     AV_NOPTS_VALUE is passed through             
	//     => AV_NOPTS_VALUE  

 

我们介绍一下AVRational类型。我们知道,在计算机中,我们是无法准确表示一个有小数的有理数的。而在FFmpeg中,则需要精确地表示有理数,为了避免这个问题,提供了AVRational以分数的形式来保存有理数,这样就可以准确表示要表达的值。

如AVRational{1,3},就表示三分之一,如果转而用浮点数表示,那么就只能表示为 0.33333 这样的只有有限位的数,并不准确。

FFmpeg还提供了针对AVRational诸多计算函数,我们下面要介绍的函数在底层可能就使用了它们。

 

下面我们详细了解一些这些API:

int64_t av_const av_gcd(int64_t a, int64_t b);

/*
	求整数a和整数b的最大公约数。
	当a和b均大于0时,返回值也大于0;当a和b有一个为0是,返回0。
	注意:负数没有最大公约数。
*/
int64_t av_rescale(int64_t a, int64_t b, int64_t c) av_const;
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const;
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const;
int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,enum AVRounding rnd) av_const;

/*
	以上4个函数是非常类似的,都是对整数a做一个缩放,准确来说是:
		a * b / c
	不同的是:
	1、b和c的类型不同,可以是整数,也可以是AVRational
	2、由于计算结果不是整数,而返回值是整数,因此需要指定舍入法。不能指定舍入法的函数使用四舍五入法。
	
	为什么要提供这几个函数?它们所做的事情貌似非常的简单,这是因为我们在计算 a*b/c 
        这个表达式时,先执行 a*b ,此时可能会导致值超出了int64_t的取值范围而导致错误,
        而这个函数则规避了这个隐藏的问题。
*/
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b);

/*
	比较两个以不同时基为单位的时间戳。
	如果 ts_a 在 ts_b 前,返回-1
	    ts_a 在 ts_b 后,返回1
	    ts_a 和 ts_b 表示同一个时间点,返回0
*/
int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod);

/*
	比较两个数的余数。
	如果 `a % mod < b % mod`,返回负值
	    `a % mod > b % mod`,返回正值
	    `a % mod == b % mod`,返回0
	    
	注意:除数mod,必须是2的幂,不能是任意整数。
*/
int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts,  AVRational fs_tb, int duration, int64_t *last, AVRational out_tb);

/*
	和av_rescale_*()的类似,返回 in_ts * in_tb / out_tb。
	
	但其使用的场景却不同,这个函数被设计为在如下情况使用:当一个流中的每个音频packet需要将input时间戳转化到其他不同的时基时,
        就使用这个函数。与简单的av_rescale_q()相比,这个函数对可能的非一致frame duration
        (即同一个流中获取的帧中保存的采样数可能会变化)具有更好的健壮性。

	参数last是一个私有的状态值,同一个流上的按照顺序获取的packet必须使用同一个last。在第一次调用时,last应被初始化为AV_NOPTS_VALUE。
	
	参数fs_tb:duration的时基,一般要比in_tb和out_tb更加的细粒度化。
	参数duration:直到下次调用这个函数的时长,也就是当前packet或frame的duration
	参数last:指向一个时间戳,时基是fs_tb,是一个私有变量
	
	注意:在这个函数的上下文中,duration是以采样为单位的,而非秒。
*/
int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t inc);

/*
	为时间戳添加一个值。
	可以注意到添加的值是以另一个时基为单位的。
	
	注意:这个函数保证,当同一个时间戳被重复添加值时不会累积舍入误差。
	代码:
	AVRational inRat = (AVRational){1,5};
        AVRational outRat = (AVRational){1,15};
        nRet = 0;
        for(int i = 0;i < 6;i++)
        {
            nRet = av_add_stable(inRat,nRet,outRat,4);
            printf("ret = %d.\n",nRet);
            //值依次为:1,3,4,5,7,8
        }
        printf("ret = %d.\n",nRet); //最终值为8而非6
*/

 

你可能感兴趣的:(FFmpeg,API,详解)