對於一個電影,幀是這樣來顯示的: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,來把時間戳從一個時基調整到另一個時基。在進行時基轉換的時候,咱們應該首選這個函數,由於它能夠避免溢出的狀況發生。