經過修改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
中的代碼。