对于一个电影,帧是这样来显示的:I B B P。如今咱们须要在显示B帧以前知道P帧中的信息。所以,帧可能会按照这样的方式来存储:IPBB。这就是为何咱们会有一个解码时间戳和一个显示时间戳 的缘由。解码时间戳告诉咱们何时须要解码,显示时间戳告诉咱们何时须要显示。因此,在这种状况下,咱们的流能够是这样的:ide
PTS: 1 4 2 3 DTS: 1 2 3 4 Stream: I P B B
一般PTS和DTS只有在流中有B帧的时候会不一样。函数
音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面。音频流有采样,视频流有每秒的帧率。然而,若是咱们只是简单的经过数帧和乘 以帧率的方式来同步视频,那么就颇有可能会失去同步。因而做为一种补充,在流中的包有种叫作DTS(解码时间戳)和PTS(显示时间戳)的机制。为了这两 个参数,你须要了解电影存放的方式。像MPEG等格式,使用被叫作B帧(B表示双向bidrectional)的方式。另外两种帧被叫作I帧和P帧(I表 示关键帧,P表示预测帧)。I帧包含了某个特定的完整图像。P帧依赖于前面的I帧和P帧而且使用比较或者差分的方式来编码。B帧与P帧有点相似,可是它是 依赖于前面和后面的帧的信息的。这也就解释了为何咱们可能在调用avcodec_decode_video之后会得不到一帧图像。编码
ffmpeg中的内部计时单位(时间基),ffmepg中的全部时间都是于它为一个单位,好比AVStream中的duration即觉得着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:spa
#define AV_TIME_BASE 1000000
ffmpeg内部时间基的分数表示,实际上它是AV_TIME_BASE的倒数。从它的定义能很清楚的看到这点:指针
#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}
AVRatioal的定义以下:code
typedef struct AVRational{ int num; //numerator int den; //denominator } AVRational;
ffmpeg提供了一个把AVRatioal结构转换成double的函数:视频
static inline double av_q2d(AVRational a){ /** * Convert rational to double. * @param a rational to convert **/ return a.num / (double) a.den; }
如今能够根据pts来计算一桢在整个视频中的时间位置:对象
timestamp(秒) = pts * av_q2d(st->time_base)
计算视频长度的方法:同步
time(秒) = st->duration * av_q2d(st->time_base)
这里的st是一个AVStream对象指针。io
因此当须要把视频跳转到N秒的时候可使用下面的方法:
int64_t timestamp = N * AV_TIME_BASE; 2 av_seek_frame(fmtctx, index_of_video, timestamp, AVSEEK_FLAG_BACKWARD);
ffmpeg一样为咱们提供了不一样时间基之间的转换函数:
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
这个函数的做用是计算a * bq / cq,来把时间戳从一个时基调整到另一个时基。在进行时基转换的时候,咱们应该首选这个函数,由于它能够避免溢出的状况发生。