FFmpeg開發——深入理解pts,dts和timebase
概述
本文將以具體視訊播放器開發過程中遇到的具體問題,來系統地闡釋pts,dts和timebase的概念。
1.時間基
在FFmpeg開發中,經常會遇到結構體中有time_base這個成員,通過標頭檔案檢視他的型別是AVRational
typedef struct AVRational{
int num; ///< numerator
int den; ///< denominator
} AVRational;
那麼AVRational到底表示了什麼呢?
AVRational這個結構標識一個分數,num為分子,den為分母。實際上time_base的意思就是時間的刻度。
如果把1秒分為25等份,你可以理解就是一把尺,那麼每一格表示的就是1/25秒。此時的time_base={1,25}。如果你是把1秒分成90000份,每一個刻度就是1/90000秒,此時的time_base={1,90000}。所謂時間基表示的就是每個刻度是多少秒。
那麼,在刻度為1/25的體系下的time=5,轉換成在刻度為1/90000體系下的時間time為(5x1/25)/(1/90000) = 3600*5=18000
正是由於不同的封裝格式,timebase是不一樣的。另外,整個轉碼過程,不同的資料狀態對應的時間基也不一致。所以在實際開發過程中,存在著大量時間基的轉換。
2.PTS和DTS
PTS:Presentation Time Stamp。PTS 主要用於度量解碼後的視訊幀什麼時候被顯示出來。
DTS
雖然 DTS、PTS 是用於指導播放端的行為,但它們是在編碼的時候由編碼器生成的。當視訊流中沒有 B 幀時,通常 DTS 和 PTS 的順序是一致的。但如果有 B 幀時,解碼順序和播放順序不一致了。
來看一個具體的例子,利用雷神做的videoeye視訊碼流分析軟體,來對視訊檔案進行分析,這個檔案是mp4格式的,可以看到視訊碼流PTS在遞增,這就是我們看到畫面的順序,但是碼流順序並不是遞增的,這裡的碼流順序可理解為解碼的順序,也就是DTS表示的意思,先解I幀,再解P幀,依次解中間的B幀
而音訊的解碼順序就是我們依次聽到的順序,PTS和DTS相等
怎麼理解PTS數值表達的含義呢,如果有某一幀,假設它是第10秒開始顯示。那麼它的pts是多少呢。是10?還是10s?還是兩者都不是。
這就引出了pts和dts的值到底代表了什麼含義這個問題
pts和dts的值指的是佔多少個時間刻度(佔多少個格子)。它的單位不是秒,而是時間刻度。只有pts與time_base兩者結合在一起,才能表達出具體的時間是多少。好比我只告訴你,某個物體的長度佔某一把尺上的20個刻度。但是我不告訴你,每個刻度是多少釐米,你仍然無法知道物體的長度。pts 就是這樣的東西,pts(佔了多少個時間刻度) ,time_base(每個時間刻度是多少秒) ,而幀的顯示時間戳 = pts(佔了多少個時間刻度) * time_base(每個時間刻度是多少秒)。
2.一些開發過程中時間基轉換的場景
1.計算視訊總時長
AVFormatContext *ifmt_ctx = NULL;
avformat_open_input(&ifmt_ctx, filename, NULL, NULL);
int totalMs;//視訊總毫秒數
totalMs = ifmt_ctx->duration / (AV_TIME_BASE / 1000);
2.根據PTS求出一幀在視訊中對應的秒數位置
double sec = enc_pkt.pts * av_q2d(ofmt_ctx->streams[stream_index]->time_base);
3.ffmpeg內部的時間戳與標準的時間轉換方法
//timestamp為ffmpeg內部時間戳,time為正常時間戳,單位為秒
timestamp = AV_TIME_BASE * time
time = AV_TIME_BASE_Q * timestamp
AV_TIME_BASE這個巨集為1000000,由此我們可以發現ffmpeg內部時間戳是以微秒(μs)為單位的
4.當需要把視訊Seek到N秒的時候
//pos單位毫秒
double pos;
seekPos = ifmtctx->streams[videoStream]->duration * pos;
av_seek_frame(ifmtctx, videoStream, seekPos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);