屏幕录制有着广泛的用途,主要用于建立演示培训,或者远程屏幕监视和广播。由于屏幕的大小和色深通常很高,而且具有大块背景色、窗口拖动/卷动造成的内容移动和菜单弹出造成的瞬时变化等特性,因此需要在数据采集、压缩和传输/存储上多加考虑。
录制之前
录制之前,应该选择录制方法,压缩方式和录制范围。
有必要尽可能缩小录制的区域以减少数据量。在录制开始之前,可以让用户指定录制的范围,例如全屏幕,指定窗口,指定/自适应坐标等等。小的录制区域意味着更少的原始数据。
尽可能降低录制的色深以减少数据量。通常,用于简单教学的录制只需要256色。
录制之前,应该停用屏幕保护,桌布和活动桌面的一些特性以避免不必要的录制。
录制方法
录制分两种方式,一种是推模式,也就是录制模块不断录制数据,一种是拉模式,录制模块在需要的时候才录制数据。对于 低带宽的场合,拉模式可以有效的减少原始数据量。对于需要基本无损回放的场合,推模式在监测到屏幕变化时就录制数据,可以减少视频质量的损失。在推模式下 需要选取录制的时机,以避免大量的重复数据。
压缩编码
适当的编码方式可以有效地减少录制时的资源占用。
- 联合图像专家组(Joint Photographic Experts Group,JPEG)编码
在有损压缩时,对于静态图像具有比较好的压缩比率。但是有损压缩可能会使得屏幕上的文字变得模糊。 - 运动图像专家(MPEG)组编码
适合运动的图像,但是剧烈变化的图像,例如菜单的弹出,可能会暂时使录制的视频质量降低,以及CPU资源占用的升高。 - 远程帧缓冲(Remote Frame Buffer,RFB)编码
包括对于窗口拖动/卷动造成的内容移动造成的矩形复制(copy rectangle)和背景色的优化。对于每个矩形,可以有各种不同的编码方式,例如MPEG或者JPEG。 - 分块比较编码
将每次录制的屏幕分块和上次录制的屏幕内容比较,如果某块抽样变化了,那么编码整个块。对于每个块,可以有各种不同的编码方式,例如MPEG或者JPEG。
根据录制内容的不同,以及编码设置的不同(例如质量和分块大小设置),以上编码方式可能各有所长。
额外的考虑
- 扫描线
隔行扫描而不是逐行扫描屏幕数据可以有效地减少录制的数据量,同时录制的视频质量降低不大。 - 录制时机
虚拟显示设备或者适当的系统钩子可以使你在屏幕变化或者即将变化时得到通知,这样可以尽可能地减少录制的次数。参见http://ultravnc.sourceforge.net 以及http://www.realvnc.com 提供的VNC源代码中的VNCHooks。 - DirectX
使 用DirectX可以进行直接的显存访问,这对提高性能很有好处。但是也由于 DirectX的特性,某些使用DirectX等技术的程序可能无法录制,例如大多数游戏和媒体播放器,即使可以录制也比较慢。录制这样的程序的窗口内容 的方法是局部或者全局地禁用部分或者全部DirectX加速功能。 - 机器码
在录制和编码过程中,使用机器码模块可以有效地加强程序的性能。例如XVID的很多核心模块就是机器码。 - 分块的大小
大的分块意味着录制时录制的次数少,但是小的分块可能意味着较少的数据量。对于依赖于背景色的编码方式,分块大小也影响数据量。
?
其他录制内容
鼠标,水印和其他图像
在录制图像的同时,可能同时需要在屏幕内容上叠加其他图像,例如鼠标或者水印。这可以通过在每一次录制的图像上在指定位置上叠加指定的图像来完成。
//Draw the Cursor
if (g_recordcursor==1) {
??? ICONINFO iconinfo ;
??? BOOL ret;
??? ret = GetIconInfo( hcur, &iconinfo );
??? if (ret) {
??????? xPoint.x -= iconinfo.xHotspot;
??????? xPoint.y -= iconinfo.yHotspot;
??????? //need to delete the hbmMask and hbmColor bitmaps
??????? //otherwise the program will crash after a while after running out of resource
??????? if (iconinfo.hbmMask) DeleteObject(iconinfo.hbmMask);
??????? if (iconinfo.hbmColor) DeleteObject(iconinfo.hbmColor);
??? }
??? :: DrawIcon( hMemDC, xPoint.x, xPoint.y, hcur);
}
这里hMemDC包含已经录制的屏幕内容,hcur表示当前缓存的光标(因为光标不是每次录制都变化,所以缓存起来效率较高)。
音频
为了丰富媒体内容,可能有必要在录制屏幕时同时录制音频。屏幕录制占用了大量资源时,有可能造成音频的录制和压缩慢于预计的频率,结果是录制的媒体音调降低,以及可能的同步问题。
加密
如果录制的内容需要保密,那么必须考虑实施加密的性能和可行性。一般来说,为每个会话产生一个不同的密钥来进行初步的加密就可以了。
?
反馈
你好,前一段时间我研究了一下VNC的源代码,发现现在最好的截屏方法是Mirror Driver,ultravnc就是用了这种技术,可是它的这部分代码并没有公开。我搜索到有一个有一个Winvncdrv也实现了这个功能,但是网上提 供的网址不能够访问了。不知道你是否有这方面编程经验或者源代码。
我的邮箱地址是zhouhuis22@sina.com,希望能够得到你的回复。
某些使用DirectX等技术的程序可能无法录制,例如大多数游戏和媒体播放器
请问:
对于使用了DirectX的游戏来说,有没有办法录制?
近段时间我被win32下的屏幕录制和屏幕广播弄得焦头烂额,
不知道大家可否交流一下这方面的经验,
这段时间里我分析了RealVNC的源码:包括VNCHook,WinVNC的屏幕截取方法,双缓冲管理方法和VNCViewer的刷写方法,
另 外,看了这篇文章后,我又看了一遍,MIRROR Driver的源码(这东西微软没有更新win 2k 的DDK和win2003 的DDK的都是一样的),对两种屏幕录制的方法(像VNC哪样在用户模式下的屏幕截取,和像一般商业的教学软件里的,在内核模式下的屏幕截取)大致了解, 但是无法结合两种方法的优点~~~~。
针对这两种屏幕录制,我有以下感觉:
VNC-like:VNC很巧妙地利用了 Win32下的消息机制,通过系统钩子的通知和GDI引擎的位传送(BitBlt)在 DibSection上完成屏幕截取,并且通过双倍桢缓冲机制减少传送像素数据所需的网络带宽,VNC能做成这样,确实是很让我惊讶,但是,在 RealVNC自己写的WinVNC里面,我觉得,Win32的消息机制会造成屏幕截取的较大延迟,而且使用了BitBlt来截屏,照样是截取不了 DirectX或电影的画面,另外,在用户模式下,VNC的内存容量、带宽的占用是很大的(在1024X768@32bpp下,需要3 X 1024X768 X4 bytes约等于4~5M的空间,另外还有3条线程在协调工作)
Mirror-Driver-like:Mirror Driver的好处是可以直接利用硬件,达到用户模式下让人难以企及的“实时”,然而,在内核模式下却好像难以利用VNC哪种钩子带来的好处(CPU占用 率低,因为需要历遍的像素数量少得多),最大的问题还是在于我不知道怎样把内核模式里面的数据怎样带到用户模式中去(因为要发送到网络上~~~~~~~) shit~~~
GetCurrentImage(...)好像可以把DirectX的图像也截取下来,不知道能不能替代BitBlt来完成所有的屏幕截呢~~~~~~~?
希望能与正在从事这方面开发的程序员交流一下
可以去看文档,有问题去邮件列表问
UltraVNC
说点经验及问题:
(1)两者差别
驱动方式(推模式)Mirror Driver:
优点:占用CPU资源少(1-5%),速度快。实时性高。
缺点:a. window 2000/xp/2003支持,98不支持。
b. 装完驱动需重新启动方能使用。
HOOK方式(拉模式)HOOK:
优点:系统支持广泛,装后不需要重启就能使用。
缺点:占用CPU资源大。
解决方法:两种都开发,window 2000/xp/2003系统采用驱动方动,其它系统使 用HOOK方式。
(2)问题
1)我已提取出了VNC中的驱动方式抓屏方法。
但在分析HOOK方式时,发现没有HOOK DLL也可以抓屏,而且CPU占有率也不是很高。怀疑是用全屏的方式定时抓的。
2)双缓冲管理方法是什么意思?有朋友可以解释一下吗?
我的MSN:fastxyf@hotmail.com,有从事这方面开发的程序员交流一下
lipinshengxp@hotmail.com
msn:icefireren@msn.com
我的邮箱:DryFisHH@hotmail.com
winvnc源码中封装了对他们的调用。可以看看源码。
况且搞一个mirror也没有什么了不起。
Mirror 怎么可以直接利用硬件呢?
读取数据我用的是读显存缓冲区,内存COPY
DirectX is much slower
发现网上的屏幕广播的软件这方面做的很不错,基本上都用了虚拟显卡Mirror的。不知哪里有这个Mirror的使用方法介绍,或者如果有直接读取显存的方法还望不吝赐教。
Bitblt的优化做的非常的精良,有兴趣的可以参考一下MSDN中的光栅操作码的介绍。
针对与截取屏幕,我以前在非常古老的计算机上作过测试, DELPHI + GDI 和 VC + DirectX的速度相近,甚至前者还略略胜出些。 所以说, 在这个方面, DirectX并无优势可言。
不过现在AGP(4x, 8x)的广泛使用以及pcix的开始, 这个瓶颈正在逐步的消失。
显存=>内存 vs 内存=>内存
对于老式机器来说(低速agp,甚至是pci),差别至少是一个数量级,甚至是两个。 对于当前的流行机器来说,应该在5倍以内。可以预见,如果pcix应用起来, 有一天从显存拷贝数据比从内存拷贝快也不是没有可能。
如果结构合理的话,这个瓶颈就自然消失了。
它应该还经过了些别的步骤吧,问题是用什么可以绕过这些步骤。
这样可以映射显存,要在内核模式下运行,取得显卡的HANDLER首先,EngDeviceIOControl要自己从导入表中算出来,效果并没有想像的好,读写显存慢的很
int MapVideoMemory(){
DWORD NumRet;
static BOOL bFirst=TRUE;
VIDEO_MEMORY videoMemory = {0};
VIDEO_MEMORY_INFORMATION videoMemoryInformation = {0};
if(globalDrv && funcList.EngDeviceIoControl && bFirst){
DbgPrint("MapVideoMemory");
__try{
VIDEO_MODE_INFORMATION modeInformation;
funcList.EngDeviceIoControl(globalDrv,IOCTL_VIDEO_QUERY_CURRENT_MODE,NULL,
0,&modeInformation,sizeof(VIDEO_MODE_INFORMATION),&NumRet);
DbgPrint("stride %d",modeInformation.ScreenStride);
DbgPrint("bitmap width %d VisScreenWidth %d",modeInformation.VideoMemoryBitmapWidth,
modeInformation.VisScreenWidth);
DbgPrint("NumberRedBits %d NumberGreenBits %d NumberBlueBits %d",modeInformation.NumberRedBits
,modeInformation.NumberGreenBits,modeInformation.NumberBlueBits);
DbgPrint("RedMask %d GreenMask %d BlueMask %d 0x%x",modeInformation.RedMask
,modeInformation.GreenMask,modeInformation.BlueMask,modeInformation.AttributeFlags);
funcList.EngDeviceIoControl(globalDrv,IOCTL_VIDEO_MAP_VIDEO_MEMORY,&videoMemory,
sizeof(VIDEO_MEMORY),&videoMemoryInformation,sizeof(VIDEO_MEMORY_INFORMATION),&NumRet);
bFirst=FALSE;
uFramebufferLength=videoMemoryInformation.FrameBufferLength;
if(videoMemoryInformation.FrameBufferLength<videoMemoryInformation.VideoRamLength){
uFramebufferLength=videoMemoryInformation.VideoRamLength;
}
pGVideo=(char*)videoMemoryInformation.FrameBufferBase;
DbgPrint("FrameBufferBase 0x%x FrameBufferLength 0x%x VideoRamBase 0x%x VideoRamLength 0x%x",
pGVideo,uFramebufferLength,videoMemoryInformation.VideoRamBase,
videoMemoryInformation.VideoRamLength);
}
__except(EXCEPTION_EXECUTE_HANDLER){
DbgPrint("Catch Error When MapVideoMemory");
}
}
CreateVideoShareMemory();
return 0;
}
这个文件找不到,请问是什么原因 ?请多多指教。我的邮箱: ljg8232068@hotmail.com
象HyperSnap那样的抓图软件截图方式很多, 其中包括扩展活动窗口截图 和 滚屏截图, 用扩展活动窗口截图方式截图时, 能设置要截取的窗口的宽和高的像素大小(可以大于屏幕的像素大小),然后截取该窗口完整的图像, 怎样编程实现呢???
另, snapit这种软件能进行DOS截屏, 又怎样编程实现呢?
象HyperSnap那样的抓图软件截图方式很多, 其中包括扩展活动窗口截图 和 滚屏截图, 用扩展活动窗口方式截图时, 能设置要截取的活动窗口的宽和高的像素大小(可以大于屏幕的像素大小),并能截取该窗口完整的图像,怎样编程实现啊???
另, 像snapit那样的抓图软件能进行DOS截屏, 又怎样编程实现呢???我想大概分以下两种情况来分析
1. 在Windows Vista, Windows XP, Windows 2000 Professional, Windows NT Workstation, Windows Me, Windows 98, or Windows 95上运行的DOS窗口时, 如何获取DOS窗口的句柄, 因为一旦获取窗口的句柄, 就能轻松编程截取该窗口的图像.在Windows XP及其后的Windows平台, 有GetConsoleWindow函数获取DOS窗口的句柄, 而Windows 98, Windows 95却没有, 怎样才能在上述所有平台上获取DOS窗口的句柄呢?? 在全屏DOS窗口下, 情况是否相同呢??
2. 在纯DOS环境下, 怎样做到按一下某个按键, 自动在后台启动指定的程序并能截取当前DOS屏幕的图像??(如果指定的程序已经启动, 则截取当前DOS屏幕的图像), 再按一下另一个键, 则退出指定的程序, 上述如何编程实现啊???
另外我也有GDI方式的屏幕捕获,效果比mirror也差不太多。
另外还有直接HOOK 显卡驱动的方式,这种方式效率应该是最高的,我还没有完全做好。
这个文件找不到,请问是什么原因 ?请多多指教。我的邮箱: ljg8232068@hotmail.com
添加sdk,应该就可以了吧
EngDeviceIoControl(globalDrv,IOCTL_VIDEO_QUERY_CURRENT_MODE,NULL,..)
函数中 globalDrv怎么获得?
我的出现了这个问题!请高手指点一下!怎么解决呢
MSN:weichuanhui@hotmail.com
内存到内存快得多
问题在于这是显存和内存的数据复制
想BitBlt和memcpy都是不符合性能要求的
另 显卡驱动不是IRP驱动的 IRP模型比较慢一点
现在国内的软件都没有国外的好,希望大家多把不涉及到商业机密的源码公开一下,大家共同进步!
哈,不知道还有谁在开发vnc?