【C#】使用ffmpeg image2pipe將圖片儲存為mp4視訊
文章目錄
需求
實現
需求
在正式開始之前,先介紹下我的需求是怎麼樣的,基於此需求如何使用ffmpeg實現。僅供參考。
需求點:
將圖片儲存為視訊
圖片數量不是固定的,是由上游的webrtc傳下的幀資料,轉成的bitmap。所以只要webrtc開著,圖片流就一直會有。
每幀影象的間隔時間依賴於不同的網路環境,所以不是固定的時間間隔。
實現
在使用原生ffmpeg之前,筆者使用了幾個第三方的nuget庫,如:FFmpeg.AutoGen、Xabe.FFmpeg、Accord.Video.FFMPEG。前兩個庫要麼只支援將資料夾裡現有的圖片儲存為mp4,要麼不支援設定每幀的PTS,導致生成的mp4播放速度太快。最後選用了Accord.Video.FFMPEG,這個庫能滿足上述的三個需求點。無奈此庫已長期不維護,當上遊的FPS>15時,WriteVideoFrame方法丟擲異常的頻率會大大提升,導致記憶體洩漏,而且當前幀也會被丟掉。
然後專案使用的是.net452,一時半會版本也升級不上去,這就過濾大多數的nuget庫。最後,只能使用的原生的ffmpeg了。
ffmpeg只是提供了一個exe,並沒有官方的API可供我們呼叫,只提供了一大堆的引數說明,真是令人頭大。經過不斷的看文件和搜尋除錯之後,發現配置以下引數可以達到我們的需求。
-f image2pipe -use_wallclock_as_timestamps 1 -i - -c:v libx264 -pix_fmt yuv420p -vsync passthrough -maxrate 5000k -an -y 123.mp4
1
以下對各引數做個簡單介紹:
image2pipe:使用圖片管道,我們可以將圖片資料一直往管道里塞,ffmpeg會不斷將其新增到mp4檔案中。用來滿足需求1和2.
use_wallclock_as_timestamps 1:開啟此選項,ffmpeg就會將接收此圖片的時間作為該幀的timestamp。這樣生成的MP4播放速度才正常,滿足需求3.
pix_fmt yuv420p:設定畫素格式,解決生成的視訊無法使用windows media player播放的問題。
-vsync passthrough:可以理解為動態幀率,根據上游的幀率來決定輸出mp4的幀率。預設有以下幾個選項:
passthrough :使用幀原始的timestamp.
cfr (1):根據輸出幀率的配置,進行自動插幀(上游幀率小於輸入幀率)或者丟幀(上游幀率大於輸入幀率)。
vfr (2):類似於passthrough, 不過當兩幀具有timestamp時會丟棄其中一個。
drop:類似於passthrough,只不過會丟棄幀原始的timstamp,然後重新生成符合幀率要求的timestamp。
auto (-1):預設行為。在cfr和vfr之前自動選擇。
maxrate:設定最大位元率
123.mp4:儲存的檔名或者路徑,注意裡面不要有空格。
最後的C#程式碼如下,我們需要使用Process類來啟動ffmpeg.exe。
public class FfmpegToVideoService { private bool _isRunning = false; private int _fps; private readonly Process _proc; /// <summary> /// Bitmap儲存為MP4 /// </summary> /// <param name="filePath">mp4要儲存的路徑,如:D:\\a\b\123.mp4</param> ///<param name="maxBitRate">最大位元率,單位K</param> public FfmpegToVideoService(string filePath,int maxBitRate = 5000) { var formattedPath = Path.GetFullPath(filePath); _proc = new Process(); //-pix_fmt yuv420p -movflags +faststart -r {30} -i pipe:.bmp -r {_fps} -timecode 00:00:00.000 //-vsync cfr自動差值 vfr根據timestamp,重複的丟棄 passthrough根據timestamp重複的不丟 -vsync passthrough //-r 30 入幀出幀都是30 _proc.StartInfo.FileName = @"ffmpeg.exe"; _proc.StartInfo.Arguments = $"-f image2pipe -use_wallclock_as_timestamps 1 -i - -c:v libx264 -pix_fmt yuv420p -vsync passthrough -maxrate {maxBitRate}k -an -y {formattedPath}"; _proc.StartInfo.WorkingDirectory = CommonFunctions.BasePath; _proc.StartInfo.UseShellExecute = false; _proc.StartInfo.RedirectStandardInput = true; _proc.StartInfo.RedirectStandardOutput = true; _proc.Start(); } // 將Bitmap資料寫入管道 private void SendToPipe(Bitmap bitmap) { if (_proc.StartInfo.RedirectStandardInput) { using (MemoryStream ms = new MemoryStream()) { bitmap.Save(ms, ImageFormat.Png); ms.WriteTo(_proc.StandardInput.BaseStream); } } } /// <summary> /// 非同步執行緒啟動服務 /// </summary> public override void StartAsync() { _isRunning = true; } /// <summary> /// 停止服務 /// </summary> public override void Stop() { _isRunning = false; try { _proc.StartInfo.RedirectStandardInput = false; _proc.StartInfo.RedirectStandardOutput = false; _proc.StandardInput.Close(); _proc.StandardOutput.Close(); _proc.Close(); } catch (Exception ex) { Log.Error(ex, ""); } } /// <summary> /// 新增item /// </summary> /// <param name="item"></param> public override void Add(FrameInfo item) { if(_isRunning) { SendToPipe(item.Bitmap); } } }
https://trac.ffmpeg.org/wiki/Slideshow
https://ffmpeg.org/ffmpeg.html#filter_005foption
https://stackoverflow.com/questions/60977555/adding-current-time-as-timestamp-in-h-264-raw-stream-with-few-frames
————————————————
版權宣告:本文為CSDN博主「JimCarter」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/catshitone/article/details/126930470