时间戳
时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
定义:当前时刻,单位是时间基。
PTS(Presentation TimeStamp)是渲染用的时间戳,播放器会根据这个时间戳进行渲染播放
DTS(Decoding TimeStamp)解码时间戳,在视频packet进行解码成frame的时候会使用到
编码时一个GOP示例: I P P B P P B P P..
由于有B帧双向参考,在编码之后的帧的时间顺序就会发生变化,导致PTS和DTS不一致。没有B帧时,两者相同。
视频时间戳:一般这个值依赖于帧率,1000/fps为帧间间隔,相当于一个个间隔时间加上去了。
pts = inc++ * (1000/fps); //其中inc是一个静态的,初始值为0,每次打完时间戳加1。
音频时间戳:依赖于音频的sample rate来计算,(1000 / sample_rate)是每个采样多长时间,(frame_size * 1000 / sample_rate)计算出来一个frame_size长度的音频帧的时长:
pts = inc++ * (frame_size * 1000 / sample_rate);
音频pts计算
音频 sample_rate : samples per second,即采样率,表示每秒采集多少采样点。 比如44100HZ,就是一秒采集44100个sample。即每个sample的时间是1/44100秒。
一个音频帧的AVFrame有nb_samples个sample,所以一个AVFrame耗时是nb_samples*(1/44100)秒
时间基
时间基表示每个刻度是多少秒,比如(1/25)秒,(1/1M)秒
时间基在ffmpeg中不是统一的,在各层中都不相同,分为:封装层时间基、编解码层时间基
封装层时间基为AVStream->time_base;编解码层时间基为AVCodecContext->time_base
定义:基本时间单位,同常用的时、分、秒、毫秒、微秒本质一样的基本时间单位
时间基的不统一,带来了时间戳之间的转换,为了提高计算精度,ffmpeg中的时间基都是以分数形式表示的有理数类型,即AVRational类型
除了上述封装层和编解码层的时间基,ffmepg还定义了一个内部时间基,AV_TIME_BASE_Q,这是一个极小的值,1微秒
有了时间戳之后,还需要将PTS的时间戳转成以秒为单位的时间。这里就需要用到ffmpeg的时间基来进行计算了。FFmepg中有三种时间基,命令⾏中tbr、tbn和tbc的打印值就是这三种时间基的倒数:
tbn: 对应容器中的时间基。 值是AVStream.time_base的倒数
tbc: 对应编解码器中的时间基。 值是AVCodecContext.time_base的倒数
tbr: 从视频流中猜算得到,可能是帧率或场率(帧率的2倍)
在ffmpeg中,不同的时间戳对应不同的时间基。一般在视频进行渲染的时候渲染的时候用到的就是视频流的时间基tbn,视频流的时间基和帧率有关,每秒30帧,代表一帧需要1/30秒。
// ffmpeg中内部的时间基
define AV_TIME_BASE 1000000
// ffmpeg中分数所表示法
define AV_TIME_BASE_Q (AVRatonal){1, AV_TIME_BASE}
时间戳的转换
存在原因:因为时间基不统一,所以当时间基变化时,需要对时间戳进行转换
不同的封装格式的timebase是不一样的。另外,整个转码过程中,不同数据状态对应的时间基也是不一致的。例如视频MPEG-TS封装格式25fps,非压缩时候的数据AVFrame,它的时间基为AVCodecContext 的time_base,AVRational{1,25}。 压缩后的数据AVPacket对应的时间基为AVStream的time_base,AVRational{1,90000}。
转换思路很简单:先将原时间戳以某一中间时间单位(这里取国际通用时间单位:秒)为单位进行转换,然后再以新的时间基为单位进行转换,即得到新的时间戳
具体转换API:由转换思路可知,转换过程即先做个乘法再做个除法,涉及到除法就有除不尽的情况,就有舍入问题,ffmpeg专门为时间戳转换提供了API,即av_rescale_q_rnd(),一定要使用该API,提高转换精度,以免给片源未来的播放带来问题。
时间戳转换为秒
要用到的API:av_q2d(time_base),先将有理数类型的时间基转为double类型
具体转换过程:time_in_second = timestamp * av_q2d(time_base)