首先介紹轉換函數:av_rescale_q_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
此函數主要用於對於不同時間戳的轉換。具體來說是將以 "時鐘基c" 表示的 數值a 轉換成以 "時鐘基b" 來表示。
FFMPEG的很多結構中有AVRationaltime_base;這樣的一個成員,它是AVRational結構的
typedef struct AVRational{
int num; ///< numerator
int den; ///< denominator
} AVRational;
AVRational這個結構標識一個分數,num爲分數,den爲分母。
實際上time_base的意思就是時間的刻度:
如(1,25),那麼時間刻度就是1/25
(1,9000),那麼時間刻度就是1/90000
那麼,在刻度爲1/25的體系下的time=5,轉換成在刻度爲1/90000體系下的時間time爲(5*1/25)/(1/90000)= 3600*5=18000
ffmpeg中做pts計算時,存在大量這種轉換
在以下結構中都有
AVCodecContext:編解碼上下文。
AVStream:文件或其它容器中的某一條流。
如果由某個解碼器產生固定幀率的碼流
AVCodecContext中的AVRational根據幀率來設定,如25幀,那麼num = 1,den=25
AVStream中的time_base一般根據其採樣頻率設定,如(1,90000)
在某些場景下涉及到PTS的計算時,就涉及到兩個Time的轉換,以及到底取哪裏的time_base進行轉換:
場景1:編碼器產生的幀,直接存入某個容器的AVStream中,那麼此時packet的Time要從AVCodecContext的time轉換成目標AVStream的time
場景2:從一種容器中demux出來的源AVStream的frame,存入另一個容器中某個目的AVStream。
此時的時間刻度應該從源AVStream的time,轉換成目的AVStream timebase下的時間。
其實,問題的關鍵還是要理解,不同的場景下取到的數據幀的time是相對哪個時間體系的。
demux出來的幀的time:是相對於源AVStream的timebase
編碼器出來的幀的time:是相對於源AVCodecContext的timebase
mux存入文件等容器的time:是相對於目的AVStream的timebase
這裏的time指pts。
轉碼時:
在解碼之前需要進行如下轉換:
packet.pts = av_rescale_q_rnd(packet.pts,
ic->streams[videoindex]->time_base,
ic->streams[videoindex]->codec->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
在編碼後寫到file之前需要進行如下轉換:
pkt.pts = av_rescale_q_rnd(pkt.pts,
oc->streams[videoindex]->codec->time_base,
oc->streams[videoindex]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));