天天看點

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

開源項目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 友善接入各種神經網絡架構處理,項目上面內建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

直播伺服器設計

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

直播伺服器通信方案我選擇的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

最後說下項目編譯相關

我主要環境在VS2017上開發。

第三方庫:

CUDA 10.1安裝:

https://developer.nvidia.com/cuda-downloads

CUDNN 10.1安裝:

https://developer.nvidia.com/cudnn

下載下傳

https://github.com/xxxzhou/oeip-thridparty

在Oeip項目下,建立一個ThirdParty檔案夾,把oeip-thridparty裡的檔案全部複制到這。 二種引用DLL方式。 一是把相應的DLL複制到對應oeip dll目錄下。 二是在環境變量裡把上面的幾個檔案夾的BIN目錄寫入,推薦第二種。(1 ThirdPartycuda 2 ThirdPartyFFmpegdll 3 ThirdPartyopencv4bin 4 ThirdPartypthreaddll).

直播SDK環境配置:

1 先啟動直播伺服器 OeipLiveServer

2 啟動媒體伺服器 OeipLiveMedia

3 本機注冊OeipLiveCom這個COM元件,然後就可以用了。

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

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

原文位址

https://www.cnblogs.com/zhouxin/p/12568967.html

繼續閱讀