2016-08-24 62 views
3

我將創建某種「遠程桌面」應用程序,將屏幕內容通過套接字流式傳輸到連接的客戶端。提高屏幕捕獲性能

爲了拍攝截圖,我想出了以下一段代碼,這是我在這裏和那裏看到的一個修改過的示例。

#include <windows.h> 
#include <tchar.h> 
#include <stdio.h> 

int _tmain(int argc, _TCHAR * argv[]) 
{ 
    int ScreenX = 0; 
    int ScreenY = 0; 
    BYTE* ScreenData = 0; 

    HDC hScreen = GetDC(GetDesktopWindow()); 

    ScreenX = GetDeviceCaps(hScreen, HORZRES); 
    ScreenY = GetDeviceCaps(hScreen, VERTRES); 
    ScreenData = (BYTE*)calloc(4 * ScreenX * ScreenY, sizeof(BYTE)); 

    BITMAPINFOHEADER bmi = {0}; 
    bmi.biSize = sizeof(BITMAPINFOHEADER); 
    bmi.biPlanes = 1; 
    bmi.biBitCount = 32; 
    bmi.biWidth = ScreenX; 
    bmi.biHeight = -ScreenY; 
    bmi.biCompression = BI_RGB; 
    bmi.biSizeImage = 0; // 3 * ScreenX * ScreenY; 


    int iBegTc = ::GetTickCount(); 

    // Take 100 screen captures for a more accurante measurement of the duration. 
    for(int i = 0; i < 100; ++i) 
    { 
     HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, ScreenX, ScreenY); 
     HDC hdcMem = CreateCompatibleDC (hScreen); 
     HGDIOBJ hOld = SelectObject(hdcMem, hBitmap); 
     BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hScreen, 0, 0, SRCCOPY); 
     SelectObject(hdcMem, hOld); 
     GetDIBits(hdcMem, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS); 
     DeleteDC(hdcMem); 
     DeleteObject(hBitmap); 
    } 

    int iEndTc = ::GetTickCount(); 

    printf("%d ms", (iEndTc - iBegTc)/100); 
    system("PAUSE"); 

    ReleaseDC(GetDesktopWindow(),hScreen); 

    return 0; 
} 

我的問題是循環內的代碼執行時間太長。在我的情況下,每次迭代大約需要36 ms。

我想知道是否有語句只能做一次,因此放在循環之外,likI爲字節緩衝區。然而,我不知道哪些是我必須爲每張新圖像做的,哪些是我只能做一次的。

+0

不確定我是否收到您的評論「//以100次屏幕截圖進行更精確的持續時間測量。」我很確定for循環的前三行可能可以在循環之外完成嗎?你試過什麼了? – LBes

+0

那麼目前我還沒有嘗試改變事情而不知道第一。我懷疑,根據我所做的錯誤,例如,由於某些內容未被更新,我可以爲每次迭代使用相同的屏幕截圖,並且由於在不實際顯示它們的情況下比較屏幕截圖並不容易,因此不切實際只是盲目嘗試。 我正在做這個循環多次,因爲根據經驗我知道GetTickCount並不總是非常準確。如果有時15毫秒不準確,那麼這個測量誤差除以迭代的次數。 – Virus721

+2

嗯,當然,但爲什麼你總是分配一些昂貴的資源(循環的前三行),而不是重複使用它們?不分配資源當然不會比分配資源慢,所以這也不是「盲目地嘗試事物」。 – IInspectable

回答

0

保持BitBltGetDIBits內循環,將剩下的外循環,如下所示:

HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, ScreenX, ScreenY); 
HDC hdcMem = CreateCompatibleDC (hScreen); 
HGDIOBJ hOld = SelectObject(hdcMem, hBitmap); 

for(int i = 0; i < 100; ++i) 
{ 
    BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hScreen, 0, 0, SRCCOPY); 
    //hBitmap is updated now 
    GetDIBits(hdcMem, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS); 
    //wait... 
} 

SelectObject(hdcMem, hOld); 
DeleteDC(hdcMem); 
DeleteObject(hBitmap); 

此外bmi.biSizeImage應設置爲數據的大小,在這種情況下4 * ScreenX * ScreenY

這不會使代碼顯着更快。瓶頸在BitBlt。它仍然約爲30幀/秒,除非屏幕上有遊戲或電影,否則這應該沒問題。

您也可以嘗試保存到24位位圖。它不會在這段代碼中產生任何影響,但數據大小將會更小((width * bitcount + 31)/32) * 4 * height)

+0

感謝您的回答。我做了這樣的事情,但不幸的是,BitBlt是計算時間的一大部分,並且對我的需求來說太慢了。我必須找到另一種方式來做到這一點。 – Virus721

+0

例如,對於DirectX遊戲還有其他選項,但就我所知,「BitBlt」是遠程桌面的唯一選項。您可以爲鼠標和鍵盤添加鉤子,僅在用戶輸入後才拍攝屏幕截圖,這將大大提高性能,並且除非窗口自己進行一些繪圖,否則應該可以。 –