美文网首页
FFmpeg中的时间戳与时间基

FFmpeg中的时间戳与时间基

作者: 张俊峰0613 | 来源:发表于2019-01-07 17:18 被阅读0次

    简介

    在开发多媒体播放器或直播系统时,音视频的同步是非常关键且复杂的点。要想把音视频同步搞明白,我们必须要了解一些基本的知识。只有了解了这些基本知识,才能为你打下理解音视频同步的基础。
    本文将从下面几个主题介绍这些知识点:

    • I/B/P帧
    • PTS/DTS
    • 时间基
    • ffmpeg的内部时间基
    • 不同时间基的换算

    I/B/P帧

    • I帧是关键帧,它采用帧内压缩技术;
    • B帧是前后参考帧,它属由帧间压缩技术。也就是说在压缩成 B帧前,它会参考它前面的非压缩视频帧,和后面的非压缩的视频帧,记录下前后两帧都不存放的“残差值”,这样可以达到更好的压缩率;
    • P帧是向前参考帧,也就是它参考的是前一个关键帧的数据。P帧也属于帧间压缩技术,相对于 B帧来说,P帧的压缩率要比B帧低。

    但在直播系统中,很少使用B帧。主要的原因是压缩和解码B帧时,由于要双向参考,所以它需要缓冲更多的数据,且使用的CPU也会更高。由于实时性的要求,所以一般不使用它。不过对于播放器来说,会经常遇到带有B帧的H264数据。

    PTS/DTS

    有了上面 I/B/P帧的概念,我们再来理解 PTS/DTS 就非常容易了。PTS(Presentation TimeStamp)是渲染用的时间戳,也就是说,我们的视频帧是按照 PTS 的时间戳来展示的。DTS(Decoding TimeStamp)解码时间戳,是用于视频解码的。

    那为什么有了 PTS 还要有 DTS呢?这就与我们上面所讲的 I/B/P帧有关了。如果我们的视频中没有B帧,那显示的帧的顺序与存放的帧的顺序是一样的,此时PTS与DTS 的值就是一样的,也就没有存在两个时间戳的必要了。视频解码与播放不是一致的

    但有了B帧之后,就不是这个样子了。我们举个简单的例子:

    第一行,实际应展示的顺序:I    B    B    P
    第二行,在缓存区存放顺序:I    P    B    B
    第三行,解码器解码的顺序:1    4    2    3
    第四行,按实际顺序号展示:1    2    3    4
    

    对于上面这个例子我们作下说明:

    1. 我们实际应该展示的帧的顺序是 I, B, B, P 帧解码后的视频帧。
    2. 但实际上,这些帧到达之后,在缓冲区里就按照第二行的样子存放的。为什么会这样呢?这是由于我上面所讲的,P帧解码时参考的是 I帧,B帧是双向参考帧。也就是说,如果 I帧和P帧没有解码的话,B帧是无法进行解码的。基于此,为了解决这个问题就出现了 PTS和DTS两个时间戳。
    3. 第三行是视频帧真正的解码顺序,先解 I帧,然后是P帧,然后是第一个B帧,最后是第二个B帧。(解码顺序)
    4. 最终的展示顺序是 I帧解码后的视频帧,第一个B帧解码后的视频帧,第二个B帧解码后的视频帧,最后是P帧解码后的视频帖。(展示顺序)

    时间基

    有了时间戳之后,最终进行展示时还要需要将 PTS时间戳转成以秒为单位的时间。那这里需要向大家介绍一下 ffmpeg的时间基。
    时间基是个有点抽象的东西, 在这里不说抽象的概念,我就把它当成时间的单位

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

    可是出现了时间基之后就不能这么处理了;

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

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

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

    时间基其实就是就是时间刻度。我们以帧率为例,如果每秒钟的帧率是 25帧,那么它的时间基(时间刻度)就是 1/25。也就是说每隔1/25 秒后,显示一帧。

    所以如我们当前的时间是 100, 时间基是 1/25,那么转成秒的时间是多少呢? 100*(1/时间基),也就是100 * 1/25 = 4秒。是不是非常的简单?

    ffmpeg内部时间基

    ffmpeg内部有一个时间基。即我们通过所见到的 AV_TIME_BASE。它在ffmpeg内部定义如下,1000 000毫秒:

    #define         AV_TIME_BASE   1000000
    

    它还有一种分数所表式法:

    #define         AV_TIME_BASE_Q   (AVRational){1, AV_TIME_BASE}
    

    在 ffmpeg中进行换算,将不同时间基的值转成按秒为单位的值计算如下:

    timestamp(秒) = pts * av_q2d(time_base)
    

    这里引入了 av_q2d 这个函数,它的定义非常简单:

    typedef struct AVRational{
    int num; //分子
    int den; //分母
    } AVRational;
    
    static inline double av_q2d(AVRational a){
    /**
    * Convert rational to double.
    * @param a rational to convert
    **/
        return a.num / (double) a.den;
    }
    

    不同时间基的换算

    ffmpeg有好几种不同的时间基,有时候我们需要在不同的时间基之间做换算。ffmpeg为我们提供了非常方便的函数。即

    av_rescale_q()
    

    av_rescale_q(a,b,c)的作用是,把时间戳从一个时基调整到另外一个时基时候用的函数。其中,a 表式要换算的值;b 表式原来的时间基;c表式要转换的时间基。其计算公式为 a * b / c。
    既然公式这么简单,我们自己写就OK了,为什么ffmpeg还要单独提供一个函数呢?其实这个看似简单的方法,还要考虑数值溢出的问题。所以把这块的逻辑加上之后,就没我们看到的这么简单了。不过没关系,我们只要清楚 av_rescale_q 是做什么的,怎么用就可以了。

    下面我再给出两个算计公式:

    • 时间戳转秒
    time_in_seconds = av_q2d(AV_TIME_BASE_Q) * timestamp
    
    • 秒转时间戳
    timestamp = AV_TIME_BASE * time_in_seconds
    

    音频的timebase是如何确定的?

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

    相关文章

      网友评论

          本文标题:FFmpeg中的时间戳与时间基

          本文链接:https://www.haomeiwen.com/subject/hsourqtx.html