Улучшение производительности захвата экрана

Я собираюсь создать какое-то приложение для "удаленного рабочего стола", которое передает содержимое экрана через сокет на подключенный клиент.

Чтобы сделать снимок экрана, я создал следующий фрагмент кода, представляющий собой модифицированную версию примеров, которые я видел здесь и там.

#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 мс на одну итерацию.

Мне интересно, есть ли операторы, которые могут быть выполнены только один раз и, таким образом, помещены вне цикла, likI сделал для байтового буфера. Однако я не знаю, какие именно я должен сделать для каждого нового изображения, и какие я могу сделать только один раз.

1 ответ

Решение

Держать BitBlt а также GetDIBits внутри цикла переместите остальную часть вне цикла следующим образом:

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)

Функция Windows Aero, похоже, влияет на скорость BitBlt.

Если итеративно вывести BitBlt даже на один пиксель от дисплея, он будет работать со скоростью около 30 кадров в секунду, а загрузка ЦП будет почти незанятой. Но если вы отключите функцию Aero в Windows, вы получите значительно более высокую скорость BitBlt.

Другие вопросы по тегам