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


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

名詞解釋

首先需要明確以下名詞概念:

  • 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來配合計算。

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

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

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

編碼實現

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

這次只需要顯示每幀的pts,time_base,time因此不需要初始化output, 只要初始化input即可。

初始化輸入源

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

    +------------------------+              +-------------------------+
    |  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)
        ...
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM