1. 程式人生 > >FFMPEG之時間基

FFMPEG之時間基

fps=29.97

fps=29.97這是一個小數啊,我如果直接利用公式 frame number = time * fps 得到了也不是一個整數啊,而幀號應該是一個整數才對。
首先,29.97f/s這個變態的數是如何得到的?這起源於早期的NTSC電視制式,而現代的數字編碼只是為了相容而沿用了它的標準。其實在標準制定時,NTSC採用的是30f/s的幀率,只是後來為了消除由彩色訊號及伴音訊號所產生的影象干擾,每秒幀幅由30幀稍微下調至29.97幀,同時頻率由15750Hz稍微下降至15734.26Hz。欲知詳情,參看Wikipedia。
然後,帶小數點的幀率如何實現呢,顯然每一秒不可能顯示相同個數的幀。實際上存在著叫做SMPTE Non-Drop-Frame和SMPTE Drop-Frame的時間同步標準,也就是說在某些時候,會通過丟掉一些幀的方式來將時間同步上。
比如剛才提到的29.97幀率,我們可以計算:29.97 f/sec = 1798.2 f/min = 107892 f/hour;
對於30f/s的幀率我們可以計算: 30 f/s = 1800 f/s = 108000 f/hour;
這樣,如果利用每秒30幀的速度來採集視訊,而用29.97f/s的速率來播放視訊,每個小時就少播放了108幀,這樣播放時間會比真實時間變慢。為了解決這個問題,SMPTE 30 Drop-Frame就採取了丟掉這108幀的方式,具體策略是每1分鐘丟兩幀,如果是第10分鐘則不丟,所以每小時丟掉的幀數是:2×60 – 2×6 = 108 幀。

time_base

FFMPEG的很多結構中有AVRational time_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:檔案或其它容器中的某一個track。

如果由某個解碼器產生固定幀率的碼流,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。
AVFrame->pts和AVPacket->pts、AVPacket->dts的值,在解碼/編碼後,會經歷短暫的time_base不匹配的情況:
解碼後,decoded_frame->pts的值使用AVStream->time_base為單位,後在AVFilter裡面轉換成以AVCodecContext->time_base為單位。   //FIXME
編碼後,pkt.pts、pkt.dts使用AVCodecContext->time_base為單位,後通過呼叫"av_packet_rescale_ts"轉換為AVStream->time_base為單位。

tbn,tbr,tbc

    ffmpeg -i video.flv
    Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 704x576 [PAR 12:11 DAR 4:3], 9578 kb/s, 25 tbr, 90k tbn, 50 tbc
    tbn = the time base in AVStream that has come from the container
    tbc = the time base in AVCodecContext for the codec used for a particular stream
    tbr = tbr is guessed from the video stream and is the value users want to see when they look for the video frame rate
不是所有引數都能得到的,有的檔案沒有這些資訊,要看解析層而定。
程式碼如下:
    if(st->codec->codec_type == CODEC_TYPE_VIDEO){
      if(st->r_frame_rate.den && st->r_frame_rate.num)
        print_fps(av_q2d(st->r_frame_rate), "tbr");

      if(st->time_base.den && st->time_base.num)
        print_fps(1/av_q2d(st->time_base), "tbn");

      if(st->codec->time_base.den && st->codec->time_base.num)
        print_fps(1/av_q2d(st->codec->time_base), "tbc");
    }

25   tbr代表幀率;
90k tbn代表檔案層(st)的時間精度,即1S=1200k,和duration相關;
50   tbc代表視訊層(st->codec)的時間精度,即1S=50,和strem->duration和時間戳相關。

 

轉載自:http://blog.csdn.net/supermanwg/article/details/14521869