ffmpeg 之 时间戳

    xiaoxiao2021-03-25  107

    看到好多人对时间戳这个概念不明白啊;简单写一下我的理解

    第一,时间戳是什么

    时间戳就是一个能够表示一个事物发生时间的东西,她有个单位,比如秒,毫秒等

    第二,timebase是个什么

    timebase是个有点抽象的东西, 在这里不说抽象的概念,你就把它当成时间的单位;

    例如25帧的视频,如果不存在timebase这个东西, 我们打时间戳应该是这样的,0-40-80-120-以此类推,40毫秒一帧图像

    可是出现了timebase之后就不能这么处理了;

    例如,同样25帧的视频, timebase设置为1/25, 那这个1/25是什么意思呢? 就是把1秒分成25份, 你的时间戳每增加1,就代表增加了1/25秒;

    视频是25帧的, 也就是每帧之间的间隔恰好就是1/25秒, 那么,我们的时间戳就可以每次递增1了;

    同理,timebase设置为1/50, 那么我们的时间戳就可以每次递增2了;

    上面三句话非常重要,是时间戳的核心原理;

    第三,如何去确定编码器的timebase

    那怎么去确定timebase是多少合适呢? 2/25可以吗?3/456可以吗?

    理论上来说都是可以的,但是有个精度问题;

    例如,同样25帧的视频,我们设置timebase为2/25;

    假设如果第一帧是时间: 0毫秒,

    换算成1/25的时间戳:0*(2/25) = 0;

    再还原成原始时间就是0/(2/25) = 0;

    在经过40毫秒后产生第二帧,这一帧的时间是40毫秒,换算成时间戳:

    40*(2/25)=3.2,由于ffmpeg记录时间戳的变量类型为long long ,所以下取整后为3

    这时候我们在还原一下原始时间:

    3/(2/25) = 37.5毫秒,下取整后为37,这就与原始时间40产生了3毫秒的误差

    实际上2/25就是1/12.5,也就是把一秒分成了12.5份,如果timebase的num/den不是帧率的整数倍都会产生误差;

    所以一般建议都设置成 1/帧率

    注意下,这个误差是帧间误差

    第四,上面为什么要计算还原后的误差?

    编码->解码这是一个永恒不变的过程,任何编码后的数据,都需要逆操作,也就是解码来复原;

    在图像中,为了更好的还原图像,以及音视频的同步,就要保证尽可能高精度的时间戳;这样我们才能更精确的知道某一幅图像应该在什么时候显示;

    第五,解码时间戳是个什么东西?

    前面说的都是编码时间戳,那么解码时间戳是什么? 从计算过程上来说,与编码时间戳是一样的;只不过这个时间代表了他应该何时被送入解码器;

    编码器会去设置这个值,用户不需要关心,只需要按编码的输出顺序送入解码器即可;这部分也不属于本文的内容,不再敖述;

    第六,第一帧图像的时间戳一定0么?

    不一定,可能是任意值,但一定是通过timebase计算得来的; 否则无法正确的知道解码后的显示顺序和时间

    第七,如果timebase是1/25,时间戳只需要每次递增1就可以吗?

    不是的,可能由于调用层卡顿,转入后台等情况导致帧率不稳定现象,也就是说很可能实际帧率是达不到设置的帧率的,如果你的时间戳依旧按照每次+1来设置,图像就会渐渐的与实际时间越拉越大;

    第八,音频的timebase是如何确定的?

    与视频类似, 只是音频换做采样率这个概念,采样率代表麦克风一秒有多少个采样产生,这个频率是极为精确的.所以我的一般做法是设置为 1/采样率; 时间戳每次递增输入的采样数;

    第九,音频和视频如何去确定起始的时间戳

    虽说时间戳不必从0开始,但是为了调试或者强迫症,我还是尽量保证从0开始...

    我的做法是这样的;

    不管音视频,上层都使用绝对的毫秒时间,编码层收到后,记录下第一个时间(音频和视频,那个早取哪个)

    例如,

    音频采样率: 44100

    视频帧率     :25

    第一个音频采样时间:140029123,记做AT1

    第一个视频帧时间   :140029143,记做VT1

    取140029123作为FRISTTIME;

    然后每次都用下面的公式进行计算即可以得出时间戳:

    用 (FRISTTIME - ATn) * (1/44100) ;

    用 (FRISTTIME - VTn) * (1/25);

    第十,ffmpeg里面的计算方式

    //目标时间戳 = f(原时间戳, 原timebase, 目标timebase, 取整方式);

    dst = av_rescale_q_rnd(src, src->time_base, dst->time_base, AV_ROUND_NEAR_INF);

    如果src是毫秒时间, 那么src的timebase是多少呢?

    考虑下上面的,1/25表示1/25秒, 1毫秒的话是1/多少?

    应该是1/1000,也就是 AVRational{1, 1000}

    当然,你应该能想到,颠倒几个参数就能还原回去,虽说还是有点点误差....

    不同模块之间的转换也是使用这个方式

    第十一,播放时如何去同步?

    同样的, 解码后,不管音视频,解码后拿到时间戳,通过前面所说的计算公式还原回去,然后都以第一个为基准, 后面的按照这个时间来计算应该显示的时间;

    转载请注明原文地址: https://ju.6miu.com/read-20261.html

    最新回复(0)