新手學習FFmpeg - 調用API計算關鍵幀渲染時間點

經過簡單的計算來,線上I幀在視頻中出現的時間點。 完整代碼請參考 https://andy-zhangtao.github.io/ffmpeg-examples/git

名詞解釋

首先須要明確如下名詞概念:github

  • I/P/B 幀(具體差別請參看 https://www.jianshu.com/p/18af03556431 )
    I幀: 內部編碼幀(關鍵幀)
    P幀: 前向預測幀(根據I幀計算差值)
    B幀: 雙向預測幀(根據I幀和P幀計算差值)
  • PTS: 幀顯示的時間刻度(在哪一個時間點顯示此幀)
  • DTS: 幀解碼的時間刻度(在哪一個時間點解碼此幀)
  • Timestamp: 幀在視頻內部的時間戳
  • Time_base: 視頻表示時間的"刻度"

處理流程

視頻內沒有絕對時間,只有相對時間(相對視頻起始位置)。例如在播放器中看到的時間進度條"00:00:05"表示的是當前看到的幀是在相對起始時間點(00:00:00)解碼並渲染的。函數

而"00:00:05"只是爲了讓用戶方便理解而展示出來的,在視頻內部則是使用時間戳來保存的,"00:00:05"可能相對的時間戳則是"5000000µs"(不考慮四捨五入)。編碼

那麼時間戳又是怎麼計算出來的呢?此時就須要經過PTS和Time_base來配合計算。code

首先來看Time_base。 Time_base比如一把尺子,上面標滿了刻度,例如(1,60)表示時間刻度就是1/60,每一個時間單位就是1/60秒。 若是是(1,1000)就表示每一個時間單位是1微秒。orm

上面說到pts是顯示的時間刻度, 也就是佔用了多少時間刻度。 換算成大白話就是pts佔用了多少個刻度,而time_base表示每一個刻度是多長。視頻

然而這有什麼用呢?Time_base最重要的做用是用來統一」時間節奏"的。 例如視頻A編碼時採用1/1000的time_base,則某個幀的pts保存爲465000。 當對視頻A進行解碼時,換成了1/9000的time_base,此時時間刻度不一致了,就須要經過pts*encode_time_base來換算成解碼時的timestamp,這樣才能保證正確解碼。get

編碼實現

上面是理論介紹,下面來看如何經過代碼來計算timestamp和換算成time.input

此次只須要顯示每幀的pts,time_base,time所以不須要初始化output, 只要初始化input便可。iframe

初始化輸入源

按照前幾篇介紹的初始化思路,只須要按照打開文件->判斷視頻流->初始化解碼器這樣的步驟就能夠了。

+------------------------+              +-------------------------+
    |  avformat_open_input   | ------------>|avformat_find_stream_info|
    +------------------------+              +-------------------------+
                                                      |
                                                      |
                                                      |
                                                     \|/
   +-----------------------------+           +-------------------------+
   |avcodec_parameters_to_context| <---------|   avcodec_find_decoder  |
   +-----------------------------+           +-------------------------+

avcodec_parameters_to_context尤爲須要關注,這個函數會根據輸入源的編碼信息來初始化用戶指定的編碼上下文。若是編碼信息不匹配或者設置錯誤時,會出現莫名的解碼錯誤。通常調用這個函數後,大多數的解碼錯誤都能消失。

計算Time

time_base是一個struct

typedef struct AVRational{
    int num; ///< Numerator
    int den; ///< Denominator
} AVRational;

num 表示的是分子,den表示分母。 對於time_base來講num就是1,den表示每一秒等分了多少份。 上面說過經過pts*time_base就能夠得出時間戳,因此須要計算出每一個時間刻度具體表明多少,因此經過av_q2d得出每一個刻度具體值。

在循環讀入解碼後的幀數據以後,能夠直接經過iframe->pts來讀取當前幀的pts值,而後再乘以刻度值就能夠得出當前時間戳iframe->pts * av_q2d(_time_base)

僞代碼以下:

while av_read_frame {
    avcodec_send_packet
    ...
    while avcodec_receive_frame {
        ...
        iframe->pts * av_q2d(_time_base)
        ...
    }
}
相關文章
相關標籤/搜索