最快的屏幕捕获方法

我想为Windows平台编写一个屏幕录像程序,但不确定如何捕获屏幕。 我知道的唯一方法是使用GDI,但我很好奇是否还有其他方法可以解决这个问题,如果有的话,哪个方法的开销最小? 速度是重中之重。

屏幕录制程序将用于录制游戏画面,但如果这样做缩小了选项的范围,我仍然可以接受任何其他不属于此范围的build议。 毕竟,知识并不坏。

编辑 :我碰到这篇文章: 各种捕获屏幕的方法 。 它向我介绍了使用Windows Media API的方法和使用DirectX的方法。 它在结论中提到禁用硬件加速可以大大提高捕获应用程序的性能。 我很好奇这是为什么。 任何人都可以为我填写缺失的空白吗?

编辑 :我读了像Camtasia的屏幕录像程序使用自己的捕获驱动程序。 有人能给我一个深入的解释,说明它是如何工作的,为什么它更快? 我也可能需要关于实现类似的指导,但是我确定无论如何都有现有的文档。

另外,我现在知道FRAPS如何logging屏幕。 它挂钩底层graphicsAPI从后台缓冲区读取。 据我所知,这是比从前缓冲区读取更快,因为你正在从系统RAM,而不是videoRAM读取。 你可以在这里阅读文章。

这是我用来收集单帧,但如果你修改这个,并保持两个目标打开所有的时间,那么你可以“stream”到磁盘使用静态计数器的文件名。 – 我不记得我在哪里find这个,但已经修改了,谢谢!

 void dump_buffer() { IDirect3DSurface9* pRenderTarget=NULL; IDirect3DSurface9* pDestTarget=NULL; const char file[] = "Pickture.bmp"; // sanity checks. if (Device == NULL) return; // get the render target surface. HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget); // get the current adapter display mode. //hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode); // create a destination surface. hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width, DisplayMde.Height, DisplayMde.Format, D3DPOOL_SYSTEMMEM, &pDestTarget, NULL); //copy the render target to the destination surface. hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget); //save its contents to a bitmap file. hr = D3DXSaveSurfaceToFile(file, D3DXIFF_BMP, pDestTarget, NULL, NULL); // clean up. pRenderTarget->Release(); pDestTarget->Release(); } 

编辑:我可以看到,这是列在您的第一个编辑链接为“GDI的方式”。 即使在该网站上的性能咨询,这仍然是一个体面的方式,你可以轻松地达到30fps我想。

从这个评论(我没有经验做这个,我只是参考一个人):

 HDC hdc = GetDC(NULL); // get the desktop device context HDC hDest = CreateCompatibleDC(hdc); // create a device context to use yourself // get the height and width of the screen int height = GetSystemMetrics(SM_CYVIRTUALSCREEN); int width = GetSystemMetrics(SM_CXVIRTUALSCREEN); // create a bitmap HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height); // use the previously created device context with the bitmap SelectObject(hDest, hbDesktop); // copy from the desktop device context to the bitmap device context // call this once per 'frame' BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY); // after the recording is done, release the desktop context you got.. ReleaseDC(NULL, hdc); // ..and delete the context you created DeleteDC(hDest); 

我并不是说这是最快的,但是如果您在兼容的设备上下文之间进行复制,则BitBlt操作通常非常快。

作为参考, Open Broadcaster软件实现了类似于“dc_capture”方法的一部分,尽pipe不是使用CreateCompatibleDC创build目标上下文IDXGISurface1 ,而是使用了与DirectX 10+兼容的IDXGISurface1 。 如果没有支持这个,他们回到CreateCompatibleDC

要将其更改为使用特定的应用程序,您需要将第一行更改为GetDC(game) ,其中game是游戏窗口的句柄,然后设置游戏窗口的正确heightwidth

一旦你在hDest / hbDesktop中的像素,你仍然需要将它保存到一个文件,但如果你正在做屏幕捕捉,那么我会认为你会想要在内存中caching一定数量的像素,并保存到video文件在大块,所以我不会指向代码保存静态图像到磁盘。

我写了一个video捕捉软件,类似于DirectX应用程序的FRAPS。 源代码是可用的,我的文章解释了一般技术。 看看http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/

尊重与您的performance有关的问题,

  • 除非你从非常缓慢的前缓冲区读取数据,DirectX应该比GDI快。 我的方法类似于FRAPS(从backbuffer中读取)。 我从Direct3D接口拦截了一组方法。

  • 对于实时录像(对应用程序影响最小),快速编解码器是必不可less的。 FRAPS使用它自己的无损video编解码器。 Lagarith和HUFFYUV是为实时应用而devise的通用无损video编解码器。 如果你想输出video文件,你应该看看他们。

  • 录制屏幕录像的另一种方法是编写一个镜像驱动程序。 根据维基百科: 当video镜像处于活动状态时,每当系统在镜像区域内的某个位置处绘制到主video设备时,实时在镜像video设备上执行绘制操作的副本。 在MSDN上查看镜像驱动程序: http : //msdn.microsoft.com/en-us/library/windows/hardware/ff568315 (v= vs.85).aspx 。

我使用d3d9获取backbuffer,并使用d3dx库将其保存到png文件中:

     IDirect3DSurface9 *表面;

     // GetBackBuffer
     idirect3ddevice9-> GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&surface);

     //保存表面
     D3DXSaveSurfaceToFileA(“filename.png”,D3DXIFF_PNG,surface,NULL,NULL);

     SAFE_RELEASE(表面);

要做到这一点,你应该创build你的swapbuffer

 d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots. 

(所以你保证在你截图之前backbuffer没有被破坏)。

我写了一个实现了屏幕截图的GDI方法的类。 我也想要额外的速度,所以在发现DirectX方法(通过GetFrontBuffer)之后,我试了一下,期待它更快。

我很惊讶地发现GDI的性能提高了2.5倍。 经过100次testing捕捉我的双监视器显示,GDI实施平均每个屏幕捕获0.65s,而DirectX方法平均1.72s。 所以根据我的testing,GDI肯定比GetFrontBuffer更快。

我无法让Brandrew的代码通过GetRenderTargetData来testingDirectX。 屏幕复制出来纯粹是黑色的。 但是,它可以超快地复制那个黑屏! 我会一直这样做,并希望得到一个工作版本,看看它的真实结果。

在我的印象中,GDI方法和DX方法在本质上是不同的。 使用GDI绘制应用FLUSH方法,FLUSH方法绘制框架,然后清除它,并在同一个缓冲区中重绘另一个框架,这将导致在需要高帧率的游戏中闪烁。

  1. 为什么DX更快? 在DX(或graphics世界)中,应用了一种更为成熟的称为双缓冲区渲染的方法,其中存在两个缓冲区,将前缓冲区提供给硬件时,也可以渲染到另一个缓冲区,然后在帧1完成渲染后,系统交换到另一个缓冲区(locking它呈现给硬件,并释放以前的缓冲区),这样大大提高了渲染效率。
  2. 为什么拒绝硬件加速更快? 尽pipe使用双缓冲区渲染,但FPS得到了改进,但渲染时间仍然有限。 现代graphics硬件通常在渲染过程中通常会涉及很多优化,如抗锯齿,这是非常计算密集型的,如果你不需要高质量的graphics,当然你可以禁用这个选项。 这会为你节省一些时间。

我认为你真正需要的是一个重播系统,我完全同意人们所讨论的内容。

对于C ++,您可以使用: http : //www.pinvoke.net/default.aspx/gdi32/BitBlt.html
这可能不适用于所有types的3D应用程序/video应用程序。 那么这个链接可能会更有用,因为它描述了3种不同的方法可以使用。

旧答案(C#):
您可以使用System.Drawing.Graphics.Copy ,但它不是很快。

我写的一个示例项目就是这样做的: http : //blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/

我打算使用Direct3D等更快的方法更新此示例: http : //spazzarama.com/2009/02/07/screencapture-with-direct3d/

这里是一个捕捉到video的链接: 如何捕捉屏幕使用C#.Net的video?

我已经能够收集到的一些东西:显然使用“镜像驱动程序”是快,虽然我不知道的OSS之一。

为什么RDP与其他远程控制软件相比如此之快?

另外显然使用StretchRect的一些卷积比BitBlt更快

http://betterlogic.com/roger/2010/07/fast-screen-capture/comment-page-1/#comment-5193

而你提到的那个(fraps挂钩到D3D dll的)可能是D3D应用程序的唯一方法,但不能用于Windows XP桌面捕获。 所以,现在我只是希望有一个fraps等价于正常桌面窗口的速度…任何人?

(我认为你可以使用类似fraps的钩子,但XP用户可能会失败)。

也明显改变屏幕位深度和/或禁用硬件加速。 可能会帮助(和/或禁用航空)。

https://github.com/rdp/screen-capture-recorder-program包含一个相当快的基于BitBlt的捕获实用程序,以及作为其安​​装的一部分的基准testing程序,它可以让您基准BitBlt速度来优化它们。;

VirtualDub也有一个“opengl”的屏幕截图模块,据说这个模块很快就可以做变化检测。http://www.virtualdub.org/blog/pivot/entry.php?id=290

你可以尝试一下c ++开源项目WinRobot @ git ,一个强大的屏幕捕获器

 CComPtr<IWinRobotService> pService; hr = pService.CoCreateInstance(__uuidof(ServiceHost) ); //get active console session CComPtr<IUnknown> pUnk; hr = pService->GetActiveConsoleSession(&pUnk); CComQIPtr<IWinRobotSession> pSession = pUnk; // capture screen pUnk = 0; hr = pSession->CreateScreenCapture(0,0,1280,800,&pUnk); // get screen image data(with file mapping) CComQIPtr<IScreenBufferStream> pBuffer = pUnk; 

支持:

  • UAC窗口
  • Winlogon中
  • DirectShowOverlay

我自己用directx做,并认为它是你想要的那样快。 我没有一个快速的代码示例,但我发现这应该是有用的。 directx11的版本不应该有很大的差异,directx9也许多一点,但是这是一条路要走的路

我意识到下面的build议不能回答你的问题,但是我发现捕获一个快速变化的DirectX视图的最简单的方法是将一个摄像机插入video卡的S-video端口,并将图像logging为电影。 然后将video从相机传输回计算机上的MPG,WMV,AVI等文件。