提高屏幕捕获性能

Improving screen capture performance

我将创建某种 "remote desktop" 应用程序,通过套接字将屏幕内容流式传输到连接的客户端。

为了截图,我想出了下面这段代码,它是我在这里和那里看到的例子的修改版本。

#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 毫秒。

我想知道是否有语句可以只执行一次并因此放在循环之外,就像我为字节缓冲区所做的那样。但是我不知道哪些是我必须为每个新图像做的,哪些是我只能做一次的。

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左右frames/sec,这应该没问题,除非屏幕上有游戏或电影。

您也可以尝试保存为 24 位位图。这段代码不会有任何区别,但数据大小会更小 ((width * bitcount + 31) / 32) * 4 * height)

Windows 的 Aero 功能似乎会影响 BitBlt 速度。

如果您从显示器迭代 BitBlt 甚至一个像素,它将 运行 以大约每秒 30 帧的速度,并且 CPU 使用将接近空闲。但是,如果您关闭 Windows 的 Aero 功能,您将获得明显更快的 BitBlt 速度。