最近在工作中遇到一個ffmpeg的坑,特此記錄下。我們在工作中,有個需求是將分段儲存的視訊拼接成一個完整的視訊,發現使用ffmpeg拼接後視訊時長不對。舉個列子,我用ffmpeg將4個半小時的mp4視訊拼接後,得到的視訊長度遠超過2小時,觀看後發現在視訊的連線點,會出現長時間的卡頓,導致最終視訊時間超長。
在ffmpeg官方文件Concatenating media files中,介紹了三種視訊拼接的方式,分別如下:
1. 針對同種編碼的視訊
可以將所有視訊檔名列到一個文字檔案中,格式如下:
file '/path/to/file1.wav'
file '/path/to/file2.wav'
file '/path/to/file3.wav'
然後使用命令ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.wav
完成對視訊的拼接,這種方式也是拼接最快的方式。大致原理是直接將視訊首位相接,不會涉及到編解碼,整體執行的時間主要是磁碟IO的時間,我們實測100個檔案,拼接成一個5g大的長視訊,也只需要幾十秒的時間。
但是,這種拼接方式有自己的侷限,首先它只能拼接相同編碼的視訊,比如都是mp4。而且,這種方式也有bug,拼接mp4視訊檔案得出來的視訊時長不對,就是我開頭所說的問題,因為這個bug我們差點改業務需求。不過這個bug可以繞過去,就是將所有mp4檔案先轉成ts檔案,然後對ts檔案拼接,拼接ts視訊不會出現這個bug。
mp4轉ts檔案的命令如下:
ffmpeg -i input.mp4 -c:v copy ouput.ts
因為mp4轉ts的過程也不涉及到視訊編解碼,所以也很快,我們也是用這種方式繞開了bug,完成了整個需求。 其實視訊拼接還有兩種方式,對我們都不太合適,後續會說到。
2.使用concat協議
ffmpeg -i "concat:input1.ts|input2.ts|input3.ts" -c copy output.ts
這個方式我們沒有具體測試,貌似不會涉及到編解碼,所以應該也挺快的,但網上說這個命令執行的條件也比較苛刻,也不推薦使用。我們沒有用的原因單純是因為需要拼接上百個視訊,這種方式需要拼一個非常長的命令列。
3. 使用Concat filter
ffmpeg -i input1.mp4 -i input2.webm -i input3.mov -filter_complex "[0:v:0][0:a:0][1:v:0][1:a:0][2:v:0][2:a:0]concat=n=3:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" output.mkv
這種使用方式還是偏複雜,具體可以參考下官方文件Concatenating media files。該方法的優點就是效果穩定、且支援不同格式的視訊,所以也是最推薦的視訊拼接方式。但缺點也很明顯,需要涉及到視訊的編解碼,所以會非常耗效能,就是因為效能問題,我們也拋棄這種方案了。
說下我們實測的資料,我們用通用伺服器,拼接60分鐘的視訊需要20-30分鐘(和伺服器配置有關),看著還行,但我們每天有數千小時的視訊需要拼接,需要幾十臺伺服器24小時滿負荷工作才能完成,對於我們當下來說成本還是偏高。 我們也委託別人試了使用GPU加速的拼接效果,確實快了很多,1小時視訊1分鐘內就可以完成。
總結
我們當前沒有GPU資源,所以當下還是選擇了使用第一種視訊拼接方式,第一種方式目前最大的瓶頸只在於網路IO(視訊下載上傳)上,但這種方案也限制了我們只能完成對視訊的拼接,無法調整其解析度以達到降低儲存的目的。長期來看我們肯定得考慮使用硬體加速的方式完成超大視訊量的處理。