2016-08-24 3 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; 
} 

내 문제는 루프 내의 코드가 너무 오래 실행되는 것입니다. 제 경우에는 반복 당 약 36ms입니다.

한 번만 수행 할 수 있고 따라서 루프 바깥에 놓을 수있는 문장이 있는지 궁금합니다. 바이트 버퍼와 비슷합니다. 그러나 나는 각각의 새로운 이미지에 대해해야만하는 것들과 내가 오직 한 번만 할 수있는 것들을 알지 못합니다. 다음

+0

"귀하의 코멘트를 얻으실 수 있습니까?"더 정확한 측정을 위해 100 개의 화면 캡처를 가져 오십시오. ". for 루프의 처음 세 줄은 아마 루프 밖에서 할 수있을 것이라고 확신합니까? 지금까지 뭐 해봤 어? – LBes

+0

글쎄 현재 나는 처음부터 알지 못하고 일을 바꾸려고하지 않았다. 나는 실수로 어떤 일을 하느냐에 따라, 예를 들어, 업데이트되지 않은 무언가로 인해 각 반복마다 동일한 스크린 샷을 가질 수 있으며 실제로 스크린 샷을 표시하지 않고 스크린 샷을 비교하는 것은 쉽지 않기 때문에 실제로는 불가능합니다. 맹목적으로 일을 시도하십시오. GetTickCount가 항상 정확하지는 않다는 것을 알고 있기 때문에이 루프를 여러 번 수행합니다. 15 밀리 초 단위로 정확하지 않은 경우이 측정 오류를 반복 횟수로 나눕니다. – Virus721

+2

그렇다면 확실히 재사용하는 대신 비용이 많이 드는 리소스 (루프의 처음 세 줄)를 지속적으로 할당하는 이유는 무엇입니까? 리소스를 할당하지 않는 것은 확실히 리소스를 할당하는 것보다 느리지 않으므로 * "맹목적인 시도"*가 아닙니다. – IInspectable

답변

0

루프 내부 BitBltGetDIBits 유지 루프 밖에 나머지 이동 :

는 또한 bmi.biSizeImage에서
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); 

이 경우 4 * ScreenX * ScreenY

에서, 데이터 크기로 설정되어야이없는 것 코드를 눈에 띄게 빨라지십시오. 병목 목은 BitBlt입니다. 여전히 약 30 프레임/초입니다. 화면에 게임이나 영화가 없으면 괜찮습니다.

24 비트 비트 맵으로 저장해보십시오. 이 코드에서는 아무런 차이가 없지만 데이터 크기는 더 작을 것입니다. ((width * bitcount + 31)/32) * 4 * height)

+0

답변 해 주셔서 감사합니다. 나는 이런 식으로 일을했지만 불행히도 BitBlt는 계산 시간의 상당 부분을 차지하는 것이지, 나의 필요에 비해 너무 느리다.나는 이것을 할 다른 방법을 찾아야 할 것이다. – Virus721

+0

예를 들어 DirectX 게임을위한 다른 옵션이 있지만, 'BitBlt'은 내가 아는 한 원격 데스크톱을위한 유일한 옵션입니다. 마우스와 키보드의 후크를 추가하고, 사용자가 입력 한 후에 만 ​​스크린 샷을 찍을 수 있습니다. 그러면 성능이 크게 향상 될 것이며, 창에서 일부 그림을 그리지 않는 한 괜찮습니다. –