Search code examples
cperformancewinapigdi

Improving screen capture performance


I am going to create some kind of "remote desktop" application that streams the content of the screen over a socket to a connected client.

In order to take a screenshot, I've come up with the following piece of code, which is a modified version of examples I've seen here and there.

#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;
}

My problem is that the code within the loop takes too long too execute. In my case it's about 36 ms per iteration.

I am wondering if there are statements that could be done just once and thus put outside of the loop, likI did for the byte buffer. I don't know however which are the ones that I must do for each new image, and which are the ones I can only do one time.


Solution

  • Keep BitBlt and GetDIBits inside the loop, move the rest outside the loop as follows:

    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);
    

    In addition bmi.biSizeImage should be set to data size, in this case 4 * ScreenX * ScreenY

    This won't make the code noticeably faster. The bottle neck is at BitBlt. It's still about 30 frames/sec, this should be okay unless there is a game or movie on the screen.

    You might also try saving to a 24 bit bitmap. It won't make any difference in this code but data size would be smaller ((width * bitcount + 31) / 32) * 4 * height)