使用“RDP 编码器镜像驱动程序”捕获屏幕






4.75/5 (10投票s)
高性能屏幕捕获方法。
引言
我的代码片段使用“RDP 编码器镜像驱动程序”截取屏幕。使用镜像驱动程序截取屏幕是一种高性能的方法,但依赖于镜像驱动程序。“RDP 编码器镜像驱动程序”包含在 Windows 7 中,使用它截取屏幕无需安装其他镜像驱动程序。
背景
当我进行驱动 LED 显示屏的工作时,我需要截取屏幕并将位图数据通过 UDP 发送到与计算机通信的硬件。使用 GDI API 截取屏幕很容易,但性能较差。我知道使用镜像驱动程序是个好主意,并开发了自己的镜像驱动程序。当我编写一个测试程序来列出所有显示设备时,我发现了“RDP 编码器镜像驱动程序”。经过测试,“RDP 编码器镜像驱动程序”运行良好。
Using the Code
首先,我们需要检测“RDP 编码器镜像驱动程序”是否存在。如果“RDP 编码器镜像驱动程序”存在,就可以使用它来截取屏幕。
int Detect_MirrorDriver(vector<DISPLAY_DEVICE>& devices,map<int,DEVMODE>& settings)
{
CString all_mirror_divers[2] = {
_T("RDP Encoder Mirror Driver"),//included in windows 7
_T("LEDXXX Mirror Driver")//my own mirror driver, used in Windows XP
};
DISPLAY_DEVICE dd;
ZeroMemory(&dd, sizeof(dd));
dd.cb = sizeof(dd);
int n = 0;
while(EnumDisplayDevices(NULL, n, &dd, EDD_GET_DEVICE_INTERFACE_NAME))
{
n++;
devices.push_back(dd);
}
for(int i=0;i<(int)devices.size();i++)
{
DEVMODE dm;
ZeroMemory(&dm, sizeof(DEVMODE));
dm.dmSize = sizeof(DEVMODE);
dm.dmDriverExtra = 0;
if(EnumDisplaySettingsEx(devices[i].DeviceName,ENUM_CURRENT_SETTINGS,&dm,EDS_ROTATEDMODE))
{
settings.insert(map<int,DEVMODE>::value_type(i,dm));
}
}
for(int m =0;m<2;m++)
{
for(int i=0;i<(int)devices.size();i++)
{
CString drv(devices[i].DeviceString);
if(drv == all_mirror_divers[m])
{
return m;
}
}
}
return -1;//can not use any mirror driver
}
使用“RDP 编码器镜像驱动程序”截取屏幕非常简单
class RDPCapture
{
int mw;
int mh;
int mx0;
int my0;
DEVMODE myDM;
DISPLAY_DEVICE myDev;
HDC m_driverDC;
CDC m_cdc;
BITMAPINFO m_BmpInfo;
HBITMAP m_Bitmap;
HBITMAP Old_bitmap;
DEVMODE oldDM;
public:
RDPCapture(){}
RDPCapture(DISPLAY_DEVICE dev,DEVMODE dm)
{
myDev = dev;
myDM = dm;
oldDM = dm;
m_driverDC = NULL;
}
~RDPCapture()
{
SelectObject(m_cdc,Old_bitmap);
DeleteObject(m_Bitmap);
m_cdc.DeleteDC();
if(m_driverDC != NULL) DeleteDC(m_driverDC);
oldDM.dmDeviceName[0] = 0;
ChangeDisplaySettingsEx(myDev.DeviceName,&oldDM, 0, 0, 0);
}
virtual bool Init(int x0,int y0,int width,int height)
{
mx0 = x0;
my0 = y0;
mw = (width + 3)&0xFFFC;
mh = height;
DEVMODE dm;
dm = myDM;
WORD drvExtraSaved = dm.dmDriverExtra;
memset(&dm, 0, sizeof(DEVMODE));
dm.dmSize = sizeof(DEVMODE);
dm.dmDriverExtra = drvExtraSaved;
dm.dmPelsWidth = 2048;
dm.dmPelsHeight = 1280;
dm.dmBitsPerPel = 24;
dm.dmPosition.x = 0;
dm.dmPosition.y = 0;
dm.dmDeviceName[0] = '\0';
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH |
DM_PELSHEIGHT | DM_POSITION;
if(ChangeDisplaySettingsEx(myDev.DeviceName, &dm, 0, CDS_UPDATEREGISTRY, 0))
{
ChangeDisplaySettingsEx(myDev.DeviceName, &dm, 0, 0, 0);
}
//------------------------------------------------
ZeroMemory(&m_BmpInfo, sizeof(BITMAPINFO));
m_BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_BmpInfo.bmiHeader.biBitCount = 24;
m_BmpInfo.bmiHeader.biCompression = BI_RGB;
m_BmpInfo.bmiHeader.biPlanes = 1;
m_BmpInfo.bmiHeader.biWidth = mw;
m_BmpInfo.bmiHeader.biHeight = -mh;
m_BmpInfo.bmiHeader.biSizeImage= mw*mh*3;
HDC Top = ::GetDC(GetDesktopWindow());
m_cdc.CreateCompatibleDC(NULL);//兼容设备上下文环境
m_Bitmap = CreateCompatibleBitmap(Top, mw,mh);//Bitmap,画布
Old_bitmap = (HBITMAP)SelectObject(m_cdc, m_Bitmap);//画布与设备上下文环境关联
::ReleaseDC(GetDesktopWindow(),Top);
m_driverDC = CreateDC(myDev.DeviceName, 0, 0, 0);
return true;
};
virtual bool GetData(unsigned char *buf)
{
BitBlt(m_cdc,0,0,mw,mh,m_driverDC,mx0,my0,SRCCOPY|CAPTUREBLT);
GetDIBits(m_cdc,m_Bitmap,0,mh,buf, &m_BmpInfo, DIB_RGB_COLORS);
return true;
};
};
要使用类“RDPCapture
”,您可以编写如下代码
RDPCapture* rdp_capture = NULL;
vector<DISPLAY_DEVICE> devices;
map<int,DEVMODE>& settings;
int res = Detect_MirrorDriver(devices,settings);
if(res ==0)
{
rdp_capture = new RDPCapture(devices[0],settings[0]);
}
关注点
当我写这篇文章时,我发现使用镜像驱动程序与 GDI API 结合使用很有趣。
历史
- 2014/1/27 首次版本
- 2014/1/27 移除 RDPCapture 的基类