新手學習FFmpeg - 調用API調整視頻局部速率

經過修改setpts代碼實現調整視頻部分的播放速率。 完整代碼可參考: https://andy-zhangtao.github.io/ffmpeg-examples/git

在前面提到了PTS/DTS/Timestamp的關係,播放器在渲染視頻時就是根據PTS來肯定渲染和展現時間點的。 根據這個原理,咱們就能夠經過調整幀的PTS時間來實現視頻加速/降速播放。github

加速/降速的原理

咱們都知道,當幀速率(frame rate)大於24時,也就是1秒播放24幀時,咱們的視覺就會看到流程的視頻。 在幀總量不變的狀況下,若是將1/24變爲1/48,那麼在相同時間內多播放了一倍的幀,對於咱們的視覺來講,就感受播放速度加快了(由於本該20秒才能播放完的幀,在10秒內就播放完了,就至關加速了一倍)。同理,若是將1/24調整爲1/12,就會看到慢動做。shell

FFmpeg提供了setpts濾鏡能夠實現調整pts的效果。 典型的用法以下:編碼

ffmpeg -i ~/tmp/trailer.mp4 -filter:v "setpts=0.5*PTS" output.mp4

0.5*PTS表示將幀的PTS值乘以0.5後做爲新的PTS值。 好比說: 幀A當前的PTS是4000(根據之前的知識,根據PTS和Time_base能夠計算出渲染的時間點)。 假設對應的時間點是: 00:00:05, 如今將PTS調整爲0.5*PTS就變成了2000,那麼對應的渲染時間點就變成了: 00:00:02.5。這樣就實現了加速播放。code

同理,若是是2*PTS就是降速播放。視頻

局部調整

setpts只能實現所有加速或者所有減速。 由於在其內部實現中,對每一個幀都應用相同的計算規則,因此要麼都調整要麼都不調整。若是要實現局部調整,按照通用的解決方案,只能先切割視頻,而後對單獨視頻進行加速/降速處理,而後再將視頻鏈接起來。繼承

但若是咱們適當調整PTS值,也能夠實現部分調整的效果。get

  • 問題分析

假設存在一段30s的視頻,幀分佈以下:it

+------------------------------------------------------------------+
        |       F1     F2     F3      F4      F5     F6      F7            |
        |       |--------------|--------------|--------------|--->         |
        |Time   0              10             20             30            |
        |PTS    0      100     200     250    300     350    400           |
        +------------------------------------------------------------------+

F1 - F7表示7個I幀(30秒包含的幀比這個多多了,這裏是爲了方便描述問題)。 假設咱們須要加速前15秒(後15秒播放速率不變)的視頻,那麼須要調整F1到F4(F4是第15秒時渲染的幀)以下:io

+------------------------------------------------------------------+
        |       F1  F2   F3   F4              F5     F6      F7            |
        |       |--------------|--------------|--------------|--->         |
        |Time   0              10             20             30            |
        |PTS    0      100     200     250    300     350    400           |
        +------------------------------------------------------------------+

這樣調整看似沒問題,但仔細分析會發如今10s-20s之間會出現天窗,這是由於這段時間內的PTS沒有任何幀須要渲染,直到第20秒的時候,纔會開始繼續渲染F5幀。顯然這樣不知足實際應用需求。

而發生問題的關鍵在於將F2-F4調整PTS以後,也須要實時調整F5-F7的PTS。 也就是正確的幀分佈應該是下面的樣子:

+------------------------------------------------------------------+
        |       F1  F2   F3   F4       F5     F6      F7                   |
        |       |--------------|--------------|--------------|--->         |
        |Time   0              10             20             30            |
        |PTS    0      100     200     250    300     350    400           |
        +------------------------------------------------------------------+

F1-F4以一個速率播放,而F5-F7以另一個速率播放。這樣就實現了部分加速的效果。

  • 代碼實現

爲了簡化編碼難度,咱們以setpts的代碼爲基礎進行修改。 在setpts代碼中修改pts的代碼是下面部分:

d = av_expr_eval(setpts->expr, setpts->var_values, NULL);
frame->pts = D2TS(d);

d是根據規則(0.5*PTS)計算出來的pts值. 而後將新的PTS賦值給當前幀,然後繼續後面的編碼處理。

因此在這裏,咱們作一些判斷,爲了簡化其它無關步驟,先假設只修改前5秒的視頻,因此須要先判斷當前幀是否須要修改:

(frame->pts * av_q2d(inlink->time_base)) < 5.0

經過pts*time_base能夠計算出當前時間點,經過這個判斷,能夠得出是否須要修改此幀的PTS值。 若是須要修改,那麼仍然經過frame->pts = D2TS(d)來調整。 而處理不須要修改的幀纔是重點,

按照上圖所示意的PTS,F5應該繼承F4調整前的PTS值,因此須要在調整F4以前須要保存舊的PTS。因此是下面的僞代碼:

保存Old PTS
    if (當前時間 < 0.5) {
        計算新的PTS並賦值給當前幀
    }else{
        當前幀使用上個幀的PTS
    }

將僞代碼實現後以下:

oldPts = frame->pts;
    if ((frame->pts * av_q2d(inlink->time_base)) < 5.0) {
        frame->pts = D2TS(d);
        setpts->_pts = frame->pts;
    } else {
        frame->pts = setpts->_pts;
    }

    setpts->_pts++;

完整代碼可參考 isetpts中的代碼。

相關文章
相關標籤/搜索