當前位置: 妍妍網 > 碼農

C# FFmpeg 音視訊開發總結

2024-02-21碼農

為什麽選擇FFmpeg?

1、延遲低,參數可控,相關函式方便查詢,是選擇FFmpeg作為編解碼器最主要原因,如果是處理即時流,要求低延遲,最好選擇是FFmpeg。

2、如果需要用Opencv或者C#的Emgucv這種庫來處理視訊流,也多是用FFmpeg做編解碼然後再轉換影像數據給Opencv去處理。 用Opencv編解碼延遲很高。

3、其他的庫多是基於FFmpeg封裝,如果做一個視訊播放器,像vlc這種庫是非常方便的,缺點是臃腫,需要手動剔除一些檔,當然也有一些是基於FFmpeg封裝好的視訊播放器庫,也能快速實作一個播放器。

4、如果是載入單Usb介面中的多Usb網路攝影機,FFmpeg這時就無能為力了,經過測試使用DirectShow能夠實作。AForge一個很好的學習樣例,它將DirectShow封裝的很好,能輕松實作載入單Usb介面中的多Usb網路攝影機(不過它很久沒更新了,目前無法設定網路攝影機參數,也沒有Usb網路攝影機直接錄制,所以我把它重寫了),當然使用其他DirectShow的庫也是可以的。

5、寫此文章時才發現CaptureManager這個2023年4月釋出的非常簡便好用的基於D3D封裝的音視訊庫,它的官方樣例非常豐富,能實作很多功能。我嘗試了執行了他的官方樣例,開啟相同規格的Usb網路攝影機,發覺cpu占用是FFmpeg的兩倍。

如何學習FFmpeg?

記錄一下我是如何學習FFmpeg。首先是C#使用FFmpeg基本上用的是FFmpeg.autogen這個庫。

也可以使用FFmpeg.exe,先不談論FFmpeg.exe的大小,我嘗試過從exe中取數據到C#前端顯示,相同參數情況下,延遲比使用FFmpeg.autogen高,主要是不能邊播放邊錄制(可以用其它的庫來錄制,但是效率比不上只使用一個庫)。當然如果只需要部份功能也可以自己封裝FFmpeg(太花時間了,我放棄了。

如果是專門從事這一行的可以試試)。學習FFmpeg.autogen可以先去Github上下載它的樣例(其實樣例有個小問題,後面說),學習基礎的編解碼。

後面有人把官網的C++的樣例用FFmpeg.autogen寫了一遍,我把樣例壓縮好放誇克網盤了:https://pan.quark.cn/s/c579aad1d8e0。

然後是檢視一些部落格和Github上一些計畫,了解編解碼整體架構,因為FFmpeg很多參考程式碼都是c++的所以我基本是參考C++寫C#,寫出整體的編解碼程式碼。無論是編解碼還是開發Fliter都會涉及到很多參數設定。

要尋找這些參數,我先是去翻部落格,最後還是去FFmpeg官網 [1] (官網文件,編解碼參數很全),當然制作視訊濾鏡和一些其他功能,也是參考官網的參數。對於部份基礎函式(有些函式會把幀用掉就釋放,要註意)檢視FFmpeg的源碼,理解原理。

對於一些概念性的東西,我是翻閱碩博論文(一般都有總結這些)。

C#使用FFmpeg需要註意什麽?

1、FFmpeg.autogen是有一個缺點的,它是全靜態的,不支持多執行緒(這個我問作者了),所以用多行程,而用多行程渲染到同一畫面,可以參考我上一篇MAF的文章。

2、尤其要註意幀釋放,編解碼的幀如果沒有釋放是一定會產生記憶體泄漏的,而且速度很快。

3、其次是c# 要將影像數據渲染到界面顯示,最最好使用WriteableBitmap,將WriteableBitmap和繫結到一個Image然後更新WriteableBitmap。我記得在一篇部落格中提到高效能渲染,使用MoveMemory來填充WriteableBitmap的BackBuffer,核心程式碼如下。

[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
privatestaticexternvoidMoveMemory(IntPtr dest, IntPtr src, uint count);
writeableBitmap.Lock();
unsafe
{
fixed (byte* ptr = intPtr)
{
MoveMemory(writeableBitmap.BackBuffer, new IntPtr(ptr), (uint)intPtr.Length);
}
}
writeableBitmap.AddDirtyRect(new Int32Rect(00, width, height));
writeableBitmap.Unlock();

這樣處理有個致命的缺點。WriteableBitamp的寬高必須為2的整數倍,即使是修正過大小,當傳入數據為特殊尺寸使用此方法時還是會出現顯示異常的情況。所以還是老實使用WriteableBitmap的WritePixels。

4、對於FFmpeg很多函式都是會返回錯誤資訊,一定要將錯誤資訊記錄到日誌,方便尋找和檢視(基本每個函式要加錯誤資訊判斷)。

5、軟編碼會占用大量的CPU資源,所以最好采用寫死。FFmpeg有一個尋找編解碼器的函式,它並不能檢視硬體編碼器。如果要使用硬體加速尋找編解碼器最好是用其他方式獲取系統裝置或者直接一個一個開啟NVDIA和QSV等加速,都失敗了再啟用軟編解碼。

6、QSV寫死要求輸入的像素格式必須為AVPixelFormat.AV_PIX_FMT_NV12,如果是硬解碼出的數據,可以直接編碼,否則需要添加格式轉換。FFmepg.autogen的官方樣例中有格式轉換函式,但由於它沒有指定轉換後的格式會出問題(踩坑)。

7、盡量少的格式轉換,或者幀復制。這兩種方式會提高cpu和記憶體使用率同時也會有更高的延遲。

8、在制作FFmpeg的帶有文本的Filter時,將需要使用的字型復制到計畫目錄然後指定字型位置而不是呼叫系統的字型(不知道是版本原因還是什麽問題,一用系統字型就會產生記憶體泄漏)。

9、註意編解碼數據的格式。一些老的格式,雖然解碼沒有什麽問題(ffmpeg 會有提示)但是編碼是不支持的,出現這種問題,程式會直接死掉(踩坑)。

10、解碼時可以透過解碼數據自動搜尋硬體解碼器,而硬體編碼需要手動指定編碼器(可以透過,尋找並自動選擇GPU來實作自動選擇)。

11、多執行緒實作播放同時錄制時,最好采用幀復制ffmpeg.av_frame_clone(hwframe)不用對同一個幀進行操作。當然也可以不用多執行緒,同一個幀在播放完成後進行,錄制。

暫時只想到這些,有其他的想法再更新,如果有任何錯誤歡迎批評指正。

相關連結

FFmpeg官網: https://ffmpeg.org/documentation.html

轉自:莫如風

連結:cnblogs.com/mrf2233/p/17442871.html

技術群: 添加小編微信並備註進群

小編微信:mm1552923

公眾號:dotNet編程大全