提高屏幕捕获性能
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 毫秒。
我想知道是否有语句可以只执行一次并因此放在循环之外,就像我为字节缓冲区所做的那样。但是我不知道哪些是我必须为每个新图像做的,哪些是我只能做一次的。
将 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左右frames/sec,这应该没问题,除非屏幕上有游戏或电影。
您也可以尝试保存为 24 位位图。这段代码不会有任何区别,但数据大小会更小 ((width * bitcount + 31) / 32) * 4 * height)
Windows 的 Aero 功能似乎会影响 BitBlt 速度。
如果您从显示器迭代 BitBlt 甚至一个像素,它将 运行 以大约每秒 30 帧的速度,并且 CPU 使用将接近空闲。但是,如果您关闭 Windows 的 Aero 功能,您将获得明显更快的 BitBlt 速度。
我将创建某种 "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 毫秒。
我想知道是否有语句可以只执行一次并因此放在循环之外,就像我为字节缓冲区所做的那样。但是我不知道哪些是我必须为每个新图像做的,哪些是我只能做一次的。
将 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左右frames/sec,这应该没问题,除非屏幕上有游戏或电影。
您也可以尝试保存为 24 位位图。这段代码不会有任何区别,但数据大小会更小 ((width * bitcount + 31) / 32) * 4 * height)
Windows 的 Aero 功能似乎会影响 BitBlt 速度。
如果您从显示器迭代 BitBlt 甚至一个像素,它将 运行 以大约每秒 30 帧的速度,并且 CPU 使用将接近空闲。但是,如果您关闭 Windows 的 Aero 功能,您将获得明显更快的 BitBlt 速度。