obs源码分析【八】:显示器采集
??obs的視屏錄制主要分3種:
- 窗口采集:采集應用程序窗口
- 顯示器采集:也叫全屏采集,可以采集整個屏幕,當有多個顯示器時,可以設置采集其中一個顯示器
- 游戲采集:可以采集游戲窗口
??在plugin-main.c可以看到各個采集的定義,代碼如下:
extern struct obs_source_info duplicator_capture_info; extern struct obs_source_info monitor_capture_info; extern struct obs_source_info window_capture_info; extern struct obs_source_info game_capture_info;??這幾個采集信息的結構體應分別在其它幾個不同的文件定義,所以用extern聲明。
??在第74行,會加載這些采集方式,代碼如下:
??由于windows系統的窗口在win8版本換了實現方式,為了采集效率,所以分兩種不同的形式進行采集。窗口采集則是BitBlt或者wcg.
??關于windows的顯示技術,可以去看微軟文檔:【顯示基礎結構的發展】
??從該文檔可知Windows 8 引入了新的 Microsoft DirectX 圖形基礎結構 (基于 DXGI) 的 API,使獨立軟件供應商能夠更輕松地 (isv) 支持桌面協作和遠程桌面訪問方案。
??本篇重點說一下win8以上的系統顯示器采集方式,Windows Desktop duplication
??Windows 8引入了新的基于 Microsoft DirectX 圖形基礎設施 (DXGI) 的 API,使獨立軟件供應商 (ISV) 可以更輕松地支持桌面協作和遠程桌面訪問方案。
??此類應用程序廣泛用于企業和教育方案。 這些應用程序有一個共同的要求:訪問桌面內容,以及將內容傳輸至遠程位置的能力。 桌面Windows 8 API 提供對桌面內容的訪問權限。
??目前,Windows API 都允許應用程序無縫實現此方案。 因此,應用程序使用鏡像驅動程序、屏幕抓取和其他專有方法來訪問桌面的內容。 但是,這些方法具有以下一組限制:
??優化性能可能很有挑戰性。
??這些解決方案可能不支持較新的圖形呈現 API,因為 API 在產品發布后發布。
??Windows并不總是提供豐富的元數據來幫助優化。
??并非所有解決方案都與 Vista 和更高版本的 Windows 中的桌面組合Windows。
??Windows 8引入了一個基于 DXGI 的 API,稱為 桌面復制 API。 此 API 通過使用位圖和關聯的元數據進行優化,提供對桌面內容的訪問。 此 API 適用于啟用的都卡主題,不依賴于應用程序使用的圖形 API。 如果用戶可以在本地控制臺上查看應用程序,則還可以遠程查看內容。 這意味著,即使全屏 DirectX 應用程序也可以復制。 請注意,API 提供保護,防止訪問受保護的視頻內容。
??API 使應用程序能夠請求Windows,以提供對沿監視器邊界的桌面內容的訪問。 應用程序可以復制一個或多個活動顯示。 當應用程序請求重復時,會發生以下情況:
??Windows呈現桌面,并向應用程序提供副本。每個呈現的幀都放置在 GPU 內存中。每個呈現的幀都附帶以下元數據:
- 臟區域
- 屏幕到屏幕移動
- 鼠標光標信息
- 向應用程序提供對幀和元數據的訪問權限。
- 應用程序負責處理每個幀:
- 應用程序可以選擇基于臟區域進行優化。
- 應用程序可以選擇使用硬件加速處理移動和鼠標數據。
- 應用程序可以選擇在流式處理之前使用硬件加速進行壓縮。
??obs的桌面復制主要是在duplicator-monitor-capture.c、monitor-capture.c以及core的libobs-d3d11,主要是用DXGI技術獲取屏幕數據,相比于GDI的截圖技術有巨大的效率提升。
??在添加采集源時,可以選擇使用DXGI技術:
??桌面采集,要達到60fps是很有難度的技術,采用DXGI是很好的選擇。
在monitor-capture.c文件中實現了很多于顯示器操作的代碼,如果有需求,可以摘取這部分代碼應用到項目中,例如:
- 枚舉顯示器
- 獲取顯示器的名字
??該文件主要是對應win7以下的顯示器桌面,采集方法用的是BitBlt, 對于win8以上的桌面OBS主要提供WCG和DXGI這兩種方式進行采集,則在duplicator-monitor-capture.c中實現,duplicator_capture_tick方法決定是采用WCG還是DXGI, 主要代碼如下:
if (capture->capture_winrt) {capture->exports.winrt_capture_free(capture->capture_winrt);capture->capture_winrt = NULL; }if (!capture->duplicator) {capture->reset_timeout += seconds;if (capture->reset_timeout >= RESET_INTERVAL_SEC) {capture->duplicator = gs_duplicator_create(capture->dxgi_index);capture->reset_timeout = 0.0f;} }if (capture->duplicator) {if (capture->capture_cursor)cursor_capture(&capture->cursor_data);//調用libobs-d3d11的導出函數gs_duplicator_update_frameif (!gs_duplicator_update_frame(capture->duplicator)) {free_capture_data(capture);} else if (capture->width == 0) {reset_capture_data(capture);} }??如果是使用DXGI, 在采集時gs_duplicator_update_frame一直會被調用,獲取桌面資源,該函數封裝在libobs-d3d11這個dll中:
EXPORT bool gs_duplicator_update_frame(gs_duplicator_t *d) {DXGI_OUTDUPL_FRAME_INFO info;ComPtr<ID3D11Texture2D> tex;ComPtr<IDXGIResource> res;HRESULT hr;if (!d->duplicator) {return false;}if (d->updated) {return true;}hr = d->duplicator->AcquireNextFrame(0, &info, res.Assign());if (hr == DXGI_ERROR_ACCESS_LOST) {return false;} else if (hr == DXGI_ERROR_WAIT_TIMEOUT) {return true;} else if (FAILED(hr)) {blog(LOG_ERROR,"gs_duplicator_update_frame: Failed to update ""frame (%08lX)",hr);return true;}hr = res->QueryInterface(__uuidof(ID3D11Texture2D),(void **)tex.Assign());if (FAILED(hr)) {blog(LOG_ERROR,"gs_duplicator_update_frame: Failed to query ""ID3D11Texture2D (%08lX)",hr);d->duplicator->ReleaseFrame();return true;}copy_texture(d, tex);d->duplicator->ReleaseFrame();d->updated = true;return true; }??這里要注意的是AcquireNextFrame的返回值,當桌面畫面沒有變化或沒有新圖像到來時會返回DXGI_ERROR_WAIT_TIMEOUT,此時無需做圖像更新操作,直接返回循環等待下一幀圖像。下面是微軟的官方說明:
AcquireNextFrame returns:S_OK if it successfully received the next desktop image. DXGI_ERROR_ACCESS_LOST if the desktop duplication interface is invalid. The desktop duplication interface typically becomes invalid when a different type of image is displayed on the desktop. Examples of this situation are: Desktop switch Mode change Switch from DWM on, DWM off, or other full-screen application In this situation, the application must release the IDXGIOutputDuplication interface and create a new IDXGIOutputDuplication for the new content. DXGI_ERROR_WAIT_TIMEOUT if the time-out interval elapsed before the next desktop frame was available. DXGI_ERROR_INVALID_CALL if the application called AcquireNextFrame without releasing the previous frame. E_INVALIDARG if one of the parameters to AcquireNextFrame is incorrect; for example, if pFrameInfo is NULL. Possibly other error codes that are described in the DXGI_ERROR topic.??獲取到紋理數據后,需要做拷貝,調用copy_texture。
??dx的接口是基于com開發的,如果不熟悉com技術,這些代碼看起來很吃力,畢竟dx是微軟最難的sdk。
??了解com技術的會,應該知道,任何基于com的對象,都得派生于IUnknown,例如DXGIObject
??IDXGIOutputDuplication派生于DXGIObject,作為結構體gs_duplicator的成員
struct gs_duplicator : gs_obj {ComPtr<IDXGIOutputDuplication> duplicator;gs_texture_2d *texture;int idx;long refs;bool updated;void Start();inline void Release() { duplicator.Release(); }gs_duplicator(gs_device_t *device, int monitor_idx);~gs_duplicator(); };??在構造時,調用Start().
??另外說明一點,采用DXGI時,obs依賴于libobs-winrt項目. 在使用obs sdk二次開發時,要注意帶上libobs-winrt生成的dll.
總結
以上是生活随笔為你收集整理的obs源码分析【八】:显示器采集的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++性能优化系列——3D高斯核卷积计算
- 下一篇: urllib库用POST请求模仿有道在线