目錄 [hide]
視頻的顯示和存放原理
對於一個電影,幀是這樣來顯示的:I B B P。現在我們需要在顯示B幀之前知道P幀中的信息。因此,幀可能會按照這樣的方式來存儲:IPBB。這就是為什么我們會有一個解碼時間戳和一個顯示時間戳的原因。解碼時間戳告訴我們什么時候需要解碼,顯示時間戳告訴我們什么時候需要顯示。所以,在這種情況下,我們的流可以是這樣的:
PTS: 1 4 2 3 DTS: 1 2 3 4 Stream: I P B B
通常PTS和DTS只有在流中有B幀的時候會不同。
DTS和PTS
音頻和視頻流都有一些關於以多快速度和什么時間來播放它們的信息在里面。音頻流有采樣,視頻流有每秒的幀率。然而,如果我們只是簡單的通過數幀和乘以幀率的方式來同步視頻,那么就很有可能會失去同步。於是作為一種補充,在流中的包有種叫做DTS(解碼時間戳)和PTS(顯示時間戳)的機制。為了這兩個參數,你需要了解電影存放的方式。像MPEG等格式,使用被叫做B幀(B表示雙向bidrectional)的方式。另外兩種幀被叫做I幀和P幀(I表示關鍵幀,P表示預測幀)。I幀包含了某個特定的完整圖像。P幀依賴於前面的I幀和P幀並且使用比較或者差分的方式來編碼。B幀與P幀有點類似,但是它是依賴於前面和后面的幀的信息的。這也就解釋了為什么我們可能在調用avcodec_decode_video以后會得不到一幀圖像。
ffmpeg中的時間單位
AV_TIME_BASE
ffmpeg中的內部計時單位(時間基),ffmepg中的所有時間都是於它為一個單位,比如AVStream中的duration即以為着這個流的長度為duration個AV_TIME_BASE。AV_TIME_BASE定義為:
#define AV_TIME_BASE 1000000
AV_TIME_BASE_Q
ffmpeg內部時間基的分數表示,實際上它是AV_TIME_BASE的倒數。從它的定義能很清楚的看到這點:
#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}
AVRatioal的定義如下:
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對象指針。
時間基轉換公式
- timestamp(ffmpeg內部時間戳) = AV_TIME_BASE * time(秒)
- time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg內部時間戳)
所以當需要把視頻跳轉到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,來把時間戳從一個時基調整到另外一個時基。在進行時基轉換的時候,我們應該首選這個函數,因為它可以避免溢出的情況發生。