視頻直播:Windows中各種畫面源的截取和合成方法總結

當今,視頻直播技術和實時音視頻技術已是不少行業必備,典型的應用場景有教育直播、遠程視頻會議、互聯網娛樂等。在移動端發起直播,其畫面源的種類是十分有限的,無非是取攝像頭、截屏等。PC端因爲其系統資源充足,應用程序豐富,畫面源種類多樣,更適合做爲主播程序運行的平臺。在實際應用中,常常有一些場景是須要將不一樣的畫面源合在一塊兒,而後推流出去的。本文粗淺介紹一些網易雲信在開發過程當中總結的一些獲取不一樣畫面源的畫面並將其合併的方法。
相關閱讀推薦
《如何快速實現移動端短視頻功能?》網絡

《視頻私有云實戰:基於Docker構建點播私有云平臺》性能

各種畫面源的截取ui

  1. 攝像頭畫面

Windows下采集攝像頭畫面,DShow是最經常使用的方法之一。經過DShow採集攝像頭數據,建立視頻採集Filter,將其加入到圖表IGraphBuilder中,用IMediaControl接口來控制流媒體在Filter Graph中的流動,再經過Render來獲取視頻的原始數據。以上流程封裝在了咱們的SDK中,用戶能夠直接調用SDK接口。視頻

  1. 桌面取屏及應用程序窗口截取

在Windows系統中,桌面和全部應用程序窗口同樣,自己也是一個HWND窗口,所以能夠放在一塊兒討論。獲取一個窗口的位圖數據,最經常使用的方法是:建立一個用來接收窗口畫面的HBITMAP位圖對象以及一個HDC設備上下文對象,用SelectObject將二者綁定,而後用BitBlt從被截取窗口的HDC將數據拷貝到目標HDC。下面列出關鍵代碼:對象

  1. 其餘截屏/截窗口方法

教育直播中,PPT分享是很是重要的一個場景。可是據我考查,自從Microsoft Office 2013以後,BitBlt就取不到Word、Excel、PPT窗口的內容了,截到的是一片白色。可是用PrintWindow這個Windows API卻能夠取到。調用PrintWindow的程序會收到WM_PRINT或WM_PRINTCLIENT消息。PrintWindow的效率比BitBlt低,但當BitBlt沒法取到時,能夠用PrintWindow。接口

愈來愈多的程序的畫面是在顯存中的,此時,BitBlt和PrintWindow都無論用(獲得的都是一塊黑色的位圖)。能夠考慮用DirectX的方法。並且DirectX方法因爲使用了GPU,因此相較前面兩種方法效率更高。如下是DirectX截屏的代碼:圖片

  1. 獲取本地圖片的位圖數據

將本地圖片(jpg、bmp、png、gif等格式)加載到內存,並取得其位圖句柄或像素首地址的方法有不少種。這裏列舉幾種最多見的。ip

GdiPlus方法比較簡單。首先是經過圖片路徑建立一個Gdiplus::Bitmap對象,經過Gdiplus::Bitmap::LockBits()方法能夠獲得圖片的數據,存放在一個Gdiplus::BitmapData結構中。Gdiplus::BitmapData::Scan0就是圖片像素數據的首地址。若是想獲得該圖片的HBITMAP句柄,只需調Gdiplus::Bitmap::GetHBITMAP()便可。內存

另外一種方法是使用Windows API LoadImage來加載一個本地bmp圖片獲得HBITMAP句柄,但這種方法彷佛只能加載位圖文件(.bmp格式)。使用ATL的CImage只須要3行代碼便可獲得一個圖片文件的HBITMAP句柄。資源

畫面合成
主播經常但願同時將本身的攝像頭畫面和桌面內容或者某個程序的畫面共享給觀衆,有時甚至須要同一時刻分享10個以上的畫面源。這時候,須要將多個畫面粘貼到一個目標畫面上,咱們稱這個過程爲畫面合成。合成的畫面一般還要支持改變各個畫面的尺寸、位置等操做。這樣一來,程序性能成了瓶頸問題。

首先,對於各類畫面源的截取應該儘可能採用高效的方式,其次,畫面的拉伸壓縮是比較耗性能的地方。在1秒鐘須要合成20幀畫面的要求下,應該避免直接強行壓縮HBITMAP,而是採用一些有加速的方案。

  1. LibYuv方案

咱們找到一個一個yuv庫(LibYuv Project),支持圖形數據從rgb格式到各類yuv格式之間的互相轉換(定義在libyuv/convert.h中)。比較重要的一點是,它對yuv格式圖形的拉伸和壓縮以及其餘各類變換(定義在libyuv/scale.h中)是有加速的。正好咱們最終要推流的格式也是yuv格式的,因此咱們方案的流程是:取得各個畫面源的畫面以後,先將它們各自轉化爲yuv格式,而後把這些yuv畫面按照咱們制定的方式粘貼到一個目標yuv畫面上,最後將目標yuv畫面數據推流出去。另外,因爲主播的窗口上也要顯示合併畫面,因此還要把目標畫面轉成rgb格式渲染到窗口HDC上。

固然,因爲存在rgb格式和yuv格式之間反覆的轉換以及頻繁的scale,並且yuv加速畢竟是軟件方式,程序的CPU佔用率仍是有點高。若是能採用DirectX、OpenGL等硬件加速解決方案,對程序性能以及用戶體驗的提高應該是比較明顯的。

  1. DirectX 9方案

在DirectX 9方案中,咱們的每一個畫面源以及最終的目標合成畫面,都對應一個表面(IDirect3DSurface9)和一個紋理(IDirect3DTexture9)。

因爲畫面源的顏色內存可能會被頻繁訪問和修改,因此建立其表面或紋理時,應該將其建立在系統內存或AGP中(D3DPOOL_MANAGED)而不是顯存中。對於yuv格式的攝像頭數據或網絡視頻幀,DirectX能夠建立能直接接受yuv數據的紋理(D3DFMT_UYVY)。合成的時候,調用IDirect3DDevice9::DrawPrimitive()來將每一個畫面源繪製到目標畫面上。

而最終合成畫面是要顯示到窗口上的,因此應該建立在顯存中(D3DPOOL_DEFAULT)。渲染的時候,調用IDirect3DDevice9::DrawPrimitive()將目標畫面的紋理繪製到窗口的渲染目標紋理上,或者調用IDirect3DDevice9::StretchRect()將目標畫面的表面粘貼到窗口的back buffer上。

另外,因爲要取得目標畫面的數據用於推流,咱們還要調用IDirect3DDevice9::CreateOffscreenPlainSurface()在系統內存中(D3DPOOL_SYSTEMMEM)建立一個離屏表面,用IDirect3DDevice9::GetRenderTargetData()將目標畫面取到離屏表面上,而後IDirect3DSurface9::LockRect()就能獲得目標畫面的rgb格式數據了,將其轉化爲yuv格式就能夠推流出去了。

總 結
直播產品因爲須要對每一幀畫面作處理,畫面的清晰度要高,幀率還不能過低,因此一般會存在消耗系統資源過多的問題。不管是取畫面仍是合成畫面,方法有不少,不只限於上面幾種。Win API效率通常,若是對程序性能要求高,就要在其餘方面去想法設法減小資源消耗。而DirectX雖然對2D圖形加速不如3D加速那麼顯著,但仍是賽過Win API的。須要注意的是,使用DirectX時要很是清楚各個參數的意義,好比設備類型(D3DDEVTYPE)、內存池類型(D3DPOOL)、用途類型(D3DUSAGE)等等。參數用錯,可能致使其性能還不如Win API。

以上就是視頻直播中Windows中各種畫面源的截取和合成方法總結。

另外,想要獲取更多產品乾貨、技術乾貨,記得關注網易雲信博客。