開源項目OEIP 游戲引擎與音視頻多媒體(UE4/Unity3D)


  現開源一個項目 OEIP  項目例子: 項目實現的功能Demo展示

  

  這個項目演示了在UE4中,接入攝像機通過OEIP直接輸出到UE4紋理上,並直接把UE4里的RenderTarget當做輸入源通過OEIP里GPU管線處理后推流出去,而另一邊Unity3D也是把RenderTarget當做輸入,用OEIP處理后推流,經過OEIP封裝signalR技術的直播SDK通知,二邊各自拉另一邊的流並通過OEIP相應管線直接輸出到Texture2D並顯示出來。演示的機器配置是i5-7500,8G內存,有二個推1080P,拉1080P流的處理,再加上生成截屏視頻和yolov3-tiny神經網絡識別,所以CPU有點吃不消。

  這是我個人驗證一些技術所搭建的DEMO級方案,接入了基本的普通攝像頭處理,也沒有提供穩定的直播供應商的實現,一些基本的圖像處理,推拉流也只支持422P/420P格式。但是我自己還是花了大量業余時間在這方案上,並以及大熱情來完善,不過業余時間畢竟有限,測試不完善,加上本人C++不是太熟悉,所以肯定有很多隱藏問題,歡迎指出問題,更歡迎提交修改。

  本項目重點主要在圖像處理並與游戲引擎的對接上,主要實現與游戲引擎對接更少的性能消耗,方便引入各種圖像處理,包括相關神經網絡圖像處理,余下處理都是結合網上代碼加上測試完善邏輯。畢竟這個項目開始只是想驗證DX11比CUDA的GPGPU計算資源占用高是不是因為線程組的分配方式,后來想着用神經網絡層的做法來搭建相關邏輯,方便用來做測試一些算法。雙十一騰訊的雲服務器打折,一時手癢就買了台,現在不是直播很火嗎,再加上對雲游戲的概念感興趣,本人在工作過程也接入過二個商業的直播SDK,通過接入SDK自己思考下流程,發現做一個技術驗證性的DEMO還是比較容易的,所以也就有了這個項目。  

  本項目暫時只考慮WIN平台,但是框架從開始就考慮從多平台擴展,后面熟悉別的平台相關知識后,會把相應功能補起。

特點:

  • 1 與游戲引擎UE4/Unity3D方便接入,引擎里的紋理可以直接傳入傳出。
  • 2 圖像處理現支持CUDA/DX11,圖像處理管線可以直接輸入輸出DX11紋理,可以做到不需要CPU/內存做圖像中轉,提高效率。
  • 3 圖像處理管線類似神經網絡框架的圖像處理層設計,並且可以動態打開與關閉某層,方便組合。
  • 4 方便接入各種神經網絡框架處理,項目上面集成yolov3(darknet),可以方便對比別的神經網絡框架接入。
  • 5 使用Media Foundation采集圖像設備,WASAPI采集麥與聲卡。
  • 6 用signalR搭建直播SDK,配合nginx管理推拉流,使用ffmpeg編碼解碼推流拉流,設計支持多推流多拉流。
  • 7 有一些同學找我要過我原來寫的CUDA grabcut實現,我是感覺效果不好也沒有商用價值,這次也集成在上面,要的可以去找相應實現自己改進。
  • 8 結合后面5G,有4K,8K圖像處理的,這種所有計算都用GPGPU來完成的應該有更多可能。

大致內容如下。

  • 1 OEIP框架設計
  • 2 GPGPU圖像處理
  • 3 采集音視頻數據
  • 4 FFmpeg編解碼與推拉流
  • 5 直播服務器設計
  • 6 Unity3D插件
  • 7 UE4插件

OEIP框架設計

  和一般直播SDK類似,分為設備采集,圖像/音頻處理,編碼,推流,服務器通知與分發,拉流,解碼,圖像顯示這幾步。

  方案中,核心項目oeip定義上面模塊的各個功能接口,插件模塊化,圖像處理層的設計。

  圖像處理層采用類神經網絡實現,層之間可以互相結合,層支持多輸入與多輸出,可以方便擴展成別的GPGPU方案,現在主要是CUDA與DX11實現,CUDA模版添加與神經網絡Darknet的集成,后續會引入別的神經網絡框架集成圖像處理。

  關聯項目:oeip

GPGPU圖像處理

  在游戲引擎里,想設計各種圖像處理說方便也方便,說麻煩也很麻煩,說方便就是因為如果你想實現的功能在這個框架下,那很簡單,嗯,UE4下如果要集成自己的Compute shader還是有點麻煩,復雜點我想引入摳圖相關算法,會發現各種麻煩,以及如果想引入 神經網絡框架的處理更是復雜,由此我想實現一個能支持CPU數據輸入,也支持引擎里GPU數據直接輸入,支持CPU數據輸出,也支持直接把處理的GPU顯存結果返回給游戲引擎,脫離實際游戲環境,只關注本身的邏輯實現。

  最開始,並沒有輸入層與輸出層的設計,但是有幾個問題,如在DX11中,讓所有層以紋理流通,而傳入與傳出的CPU數據與紋理長度不一定對應是其一,其二封裝內存/顯存處理,顯存外部上下文與Oeip處理的上下文不同線程切換等,三是並不好處理多輸入與多輸出,中間層輸出等各種問題,所以加入輸入與輸出層,這二層本身並沒任何邏輯,專門用來解決上面的問題。

  在GPU算法中,一是善用一些多線程的算法,如跨線程組步長的循環,以及線程組內二分操作,盡可以同時多利用線程組內所有線程。二是多利用共享顯存,注意這個大小有限制,如果你把太多數據放進去,可能會起反作用。三是GPGPU線程組的划分也比較重要,如果出現幾個線程同時訪問或是讀取某個顯存地址,不管需要同步不,都不算太好的方式,情願一個線程讀寫多個顯存地址。四是可以在CPU確認判斷可以先編譯成不同GPU代碼,如HLSL可以通過加入宏定義編譯,而CUDA可以利用模版。五減少與CPU的數據交互,如1080P的數據下,上傳與下載到顯存的時間大約是你做一次基本圖像處理的十倍左右,我認為的理想方法,要么是從CPU數據讀入,然后所有處理在GPU,並通過引擎顯示,或是數據就在GPU上,圖像處理最后一步交給CPU傳輸用,或是從GPU來,GPU處理后再還給GPU,中間但凡出現多次CPU-GPU的交互不如考慮方案的合理性。

  關聯項目:oeip-win,oeip-win-cuda,oeip-win-dx11

  CUDA版Grabcut的實現 整合Yolov3到UE4/Unity3D

采集音視頻數據

  這個沒什么好說的,采集圖像視頻用的是Media Foundation技術,大約有幾點,一是讀不管異步還是同步,數據讀取都應該放在非主線程中,用異步讀自己不需要開,用同步自己管理線程,但是需要注意設備關閉時,確保相應數據流線程最好同步調用線程關閉,免的數據狀態不正確。二是避免CPU處理數據,直接讀取設備所支持的原生格式,如NV12(YUV420SP),YUV422I,我們在GPGPU圖像處理層里有相應的YUV/RGB層,層里采集設備常用的NV12,YUV420I,BGR,YUV422I等都支持,當然傳輸用的YUV422P,YUV420P也是支持的,相應的CUDA/HLSL代碼都有.三是我以前采過的坑,采集設備就是采集數據,他本身不應該和數據處理綁在一起。

  音頻采集用的WASAPI技術,處理沒用Media Foundation,重采樣,混音用的FFmpeg,音頻采集主要是麥與聲卡這二部分,麥還好,聲卡處理需要注意靜音的處理,別的跟着網上的代碼來就行。

  關聯項目:oeip-win-mf,oeip-ffmpeg

FFmpeg編解碼與推拉流

  現在直播相關比較火,並且結合現在網絡情況可以做很多原來想不到的事情,雲游戲這種原概念產品感覺有完善的可能了,我今年也學了些FFmpeg相關知識用來儲備。

  推流前,數據處理后需要編碼,主要用來壓縮數據,可以說是超強的壓縮率,在這只結合網上代碼完善了H264與AAC這二種視頻與音頻編碼方式,推拉流使用RTMP協議。

  而拉流就是把上來的拉到的H264/AAC數據解碼得到YUV/PCM固定格式后固定大小的數據,然后自己處理。

  主要代碼都是參照網上部分,然后整合,其中感覺主要是FFmpeg各種資源的銷毀比較麻煩,比如要動態更新編碼格式,重采樣混音都有FFmpeg中間重用的資源,結合std::unique_ptr可以自定義銷毀函數與模板,寫出C#的感覺,省了我不少腦力與代碼。

  關聯項目:oeip-ffmpeg  

  音視頻開發-FFmpeg

直播服務器設計

  直播服務器簡單來說,就是通知一組成員之間消息流通,比如張三李四王五,張三上來了,李四推流了,王五關閉推流了等等這些消息,都需要及時通知這組里的所有成員,每個成員根據需求來對各種消息做各種處理。

  直播服務器通信方案我選擇的signarR,我對C#相關的技術熟悉點。

  這只是一個非常簡單的設計,主要分為三方,一是SDK調用方,也就是上面的張三李四王五他們,二是直播服務器,管理上面的各種通知,三是媒體服務器,管理推拉流的音視頻數據。三方是可以分開放的,不過現沒有丟楨方案,SDK調用方最好和媒體服務器在同一局域網效果會比較好。

  相關流程簡單來說,先打開直播服務器,然后打開媒體服務器,這樣直播服務器就知道了所有的媒體服務器,然后SDK調用方連接直播服務器后,直播服務器返回給SDK調用方相應的媒體服務器地址,這樣SDK調用方推流后就知道向那個媒體服務器的地址推流並記錄下來,然后別的用戶進來,就通知別的用戶已經有別的用戶推流了,並返回相應的推流地址,然后就可以拉流的,當然這個用戶推流了,也需要返回相應推流地址給前一個用戶。

  注意事項,signalR 現在也是類似ASP net core里的一個中間件,在這為了直播服務器是否成功打開,我也寫了個簡單的中間件驗證是否能成功連接服務器,打開服務器就會返回結果,在這中間件處理的是每個請求,每次請求都會生成一個HUB對象,這樣導致相應的HUB里面綁定事件話,會累加,所以並不是一個好的選擇,可以用GlobalHost返回這個HUB邏輯上的所有鏈接用戶。

  SDK調用方,我最開始找的是signalR的C++實現,可惜,一個是老版概念signalR 實現的,幾年沒更新了,最新的在asp net core下有份C++ 實現,這個還沒BATA版,故客戶端SDK調用方與直播服務器通信用C#完成,我們知道,與播服務器通信主要是二個部分,一個是我們主動提交的信息,如我們登陸了,我們推流了,還有一個是直播服務器的通知,比如通知你別的用戶上線,別的用戶推流了。第一個部分我們主動發起通知,表現就是我們從C++調用相關C#的實現,而第二部分是服務器通知回調,需要從C#端通知到C++端,這個算是不常用調用方法,綜合考慮了下,把相應的C#客戶端封裝成COM接口,方便一是C++調用相關C#的實現,二是把相應的C++接口實現傳入到C#環境中去執行。需要注意的,這個C++客戶端事實上包含相應的CLI環境,所以如果銷毀資源,如unity3D/UE4里的每次play/endplay間,要確認引用的C++DLL所關聯的CLI環境已經清理干凈,我反正是在對應銷毀時調用GC.WaitForPendingFinalizers()才搞定關閉時不掛起的現象。

  需要注意的是,客戶端C#使用COM封裝,那么每台機器需要注冊相應的COM組件,如果你是用的VS,直接開管理員,編譯相應的OeipLiveCom項目就行。

  當然這個等asp net core signalR的C++實現完善后,會把相應C#+COM/C++調用方案改成全C++低層實現。

  關聯項目:OeipLiveServer,OeipLiveMedia,OeipLiveCom,oeip-live,oeip-live-ffmpeg

Unity3D插件

  因為在驗證各項功能前,我已經用WinForm+SharpDx做了驗證項目,包含DX11紋理的傳入傳出驗證,Unity3D的大部分代碼和這部分共用,注意事項就一點,在Unity3D C#中我們拿不到DX11設備與上下文,我們需要編寫一個Unity3D的非托管插件,在這插件里我們能拿到Unity3D的DX11設備與上下文,結合OEIP原來接口再封裝一層。

  注意事項,更新Unity3D的RHI資源,需要用到Unity3D的非托管插件特定的寫法,保證在渲染線程中更新資源,而OEIP回調大部分在非主線程中,所以回調里要用到Unity3D游戲線程里的資源里,請轉到游戲線程去執行。

  關聯項目:oeip-unity3d,OeipWrapper,OeipUnity3D

  更詳細說明請看 UE4/Unity3D中同時捕獲多高清攝像頭的高效插件

UE4插件

  基本和Unity3D插件思路一樣,相應數據處理編寫相應管線,設備數據處理管線,拉流管線,推流管線,直播SDK的再封裝都是差不多的,就連注意事項也是差不多,回調里用到UE4資源的,請轉到游戲線程,用到RHI資源的,請轉到渲染線程。

  關聯項目:OeipUE4

  更詳細說明請看 UE4/Unity3D中同時捕獲多高清攝像頭的高效插件

最后說下項目編譯相關  

  我主要環境在VS2017上開發。

  第三方庫:

  CUDA 10.1安裝:

  CUDNN 10.1安裝:

  下載在Oeip項目下,新建一個ThirdParty文件夾,把oeip-thridparty里的文件全部復制到這。 二種引用DLL方式。 一是把相應的DLL復制到對應oeip dll目錄下。 二是在環境變量里把上面的幾個文件夾的BIN目錄寫入,推薦第二種。(1 ThirdParty\cuda 2 ThirdParty\FFmpeg\dll 3 ThirdParty\opencv4\bin 4 ThirdParty\pthread\dll).

  直播SDK環境配置:

  • 1 先啟動直播服務器 OeipLiveServer
  • 2 啟動媒體服務器 OeipLiveMedia
  • 3 本機注冊OeipLiveCom這個COM組件,然后就可以用了。

  相應UE4/Unity3D里神經網絡加載用的的絕對路徑,請自己修改相應路徑。

  其主要只考慮了64位,相應編譯的環境只有64位配置了,32位需要自己配置。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM