Windows上最快的屏幕捕获方法

som*_*guy 149 c c++ windows

我想为Windows平台编写一个截屏程序,但我不确定如何捕获屏幕.我所知道的唯一方法就是使用GDI,但我很好奇是否还有其他方法可以解决这个问题,如果有的话,会产生最少的开销?速度是一个优先事项.

屏幕录像程序将用于录制游戏镜头,但是,如果这确实缩小了选项范围,我仍然可以接受任何其他超出此范围的建议.毕竟知识还不错.

编辑:我遇到过这篇文章:捕获屏幕的各种方法.它向我介绍了Windows Media API的实现方式以及DirectX的实现方式.它在结论中提到,禁用硬件加速可以极大地提高捕获应用程序的性能.我很好奇为什么会这样.任何人都可以为我填补遗失的空白吗?

编辑:我读过像Camtasia这样的截屏程序使用他们自己的捕获驱动程序.有人能给我一个深入的解释它是如何工作的,以及为什么它更快?我可能还需要有关实现类似内容的指导,但我确信无论如何都有现有的文档.

此外,我现在知道FRAPS如何记录屏幕.它挂钩底层图形API以从后台缓冲区读取.根据我的理解,这比从前端缓冲区读取更快,因为您是从系统RAM而不是视频RAM读取的.你可以在这里阅读这篇文章.

小智 57

这是我用来收集单帧的内容,但是如果你修改它并保持两个目标一直打开,那么你可以使用文件名的静态计数器将它"流"到磁盘. - 我不记得我在哪里发现了这个,但它已被修改,多亏了谁!

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();
}
Run Code Online (Sandbox Code Playgroud)

  • downvote,它只适用于您自己的应用程序,因此不能用于记录通用程序 (11认同)

dar*_*s0n 29

编辑:我可以看到,这是在您的第一个编辑链接下列为"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);

// ..delete the bitmap you were using to capture frames..
DeleteObject(hbDesktop);

// ..and delete the context you created
DeleteDC(hDest);
Run Code Online (Sandbox Code Playgroud)

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

作为参考,Open Broadcaster Software实现了类似这样的东西作为其"dc_capture" 方法的一部分,尽管不是hDest使用CreateCompatibleDC它们创建目标上下文,而是使用IDXGISurface1与DirectX 10+ 一起使用的目标上下文.如果没有对此的支持,他们就会回归CreateCompatibleDC.

要改变它使用一个特定的应用程序,您需要更改的第一行到GetDC(game)这里game是游戏的窗口的句柄,然后设置权heightwidth游戏的窗口太多.

一旦你有hDest/hbDesktop中的像素,你仍然需要将它保存到一个文件,但如果你正在进行屏幕捕获,那么我认为你想要在内存中缓冲一定数量的像素并保存到视频文件中在块中,所以我不会指向将静态图像保存到磁盘的代码.

  • 不要使用GDI写一个截屏程序 - 它太慢!!!!! (6认同)
  • http://msdn.microsoft.com/en-us/library/dd183370%28VS.85%29.aspx摘录:*如果源和目标设备上下文的颜色格式不匹配,BitBlt函数会转换源颜色格式以匹配目标格式.* (3认同)
  • 一些证据证明这一点很好.您是否在某处发布了性能比较,或者看过准确的比较? (2认同)
  • 尝试分析它.正如我在帖子中所说,我引用的是有GDI经验的人.如果它泄漏内存,并且您知道如何修复它,请编辑帖子以消除泄漏. (2认同)

Her*_*nán 17

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

尊重与绩效相关的问题,

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

  • 对于实时视频录制(对应用程序影响最小),快速编解码器至关重要.FRAPS使用它自己的无损视频编解码器.Lagarith和HUFFYUV是专为实时应用而设计的通用无损视频编解码器.如果要输出视频文件,应该查看它们.

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


bob*_*obo 15

我使用d3d9来获取后台缓冲区,并使用d3dx库将其保存到png文件中:

    IDirect3DSurface9 *surface ;

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

    // save the surface
    D3DXSaveSurfaceToFileA( "filename.png", D3DXIFF_PNG, surface, NULL, NULL ) ;

    SAFE_RELEASE( surface ) ;

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

d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.
Run Code Online (Sandbox Code Playgroud)

(所以你保证在拍摄截图之前后备缓冲区没有被破坏).


zin*_*ing 11

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

  1. 为什么DX更快?在DX(或图形世界)中,应用了一种称为双缓冲渲染的更成熟的方法,其中存在两个缓冲区,当前缓冲区存在于硬件时,您也可以渲染到另一个缓冲区,然后在帧1之后完成渲染后,系统交换到另一个缓冲区(将其锁定以呈现给硬件,并释放前一个缓冲区),这样就大大提高了渲染效率.
  2. 为什么更快地降低硬件加速?虽然使用双缓冲区渲染,但FPS得到了改进,但渲染时间仍然有限.现代图形硬件在渲染过程中通常需要进行大量优化,例如抗锯齿,这是非常耗费计算的,如果你不需要高质量的图形,当然你可以禁用这个选项.这会节省你一些时间.

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


rot*_*mod 9

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

我很沮丧地发现GDI的速度提高了约2.5倍.在100次试验捕获我的双显示器显示后,GDI实现平均每屏截屏0.65秒,而DirectX方法平均为1.72秒.因此,根据我的测试,GDI肯定比GetFrontBuffer快.

我无法让Brandrew的代码通过GetRenderTargetData测试DirectX.屏幕副本纯粹是黑色的.但是,它可以超快地复制那个空白屏幕!我会继续修补它,并希望得到一个工作版本,看看它的真实结果.


Ted*_*sen 8

对于C++,您可以使用:http :
//www.pinvoke.net/default.aspx/gdi32/BitBlt.html这可能不适用于所有类型的3D应用程序/视频应用程序.然后此链接可能更有用,因为它描述了您可以使用的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/

以下是捕获视频的链接:如何使用C#.Net将屏幕捕获为视频

  • 啊,我忘了提到我用C编程(可能是C++)并且我不打算使用.NET.非常抱歉 :/. (2认同)

rog*_*ack 8

我已经能够收集的一些东西:显然使用"镜像驱动程序"虽然我不知道OSS.

与其他遥控软件相比,为什么RDP如此之快?

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

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

你提到的那个(连接到D3D dll)可能是D3D应用程序的唯一方法,但不适用于Windows XP桌面捕获.所以现在我只是希望普通的桌面窗口有一个相当于速度的fraps ...任何人?

(我认为使用aero你可能会使用类似fraps的钩子,但XP用户会运气不好).

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

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

VirtualDub还有一个"opengl"屏幕捕获模块,据说可以快速执行更改检测等操作http://www.virtualdub.org/blog/pivot/entry.php?id=290


Ben*_*per 6

您需要桌面复制 API(自 Windows 8 起可用)。这是官方推荐的方法,也是 CPU 效率最高的方法。

它用于截屏的一个很好的功能是它检测窗口移动,因此您可以在窗口移动时传输块增量,而不是原始像素。此外,它会告诉您哪些矩形发生了变化,从一帧到下一帧。

微软的示例代码相当复杂,但API其实简单易用。我整理了一个简单得多的示例项目:

简化的示例代码

Windows 桌面复制示例

微软参考

桌面复制 API

官方示例代码(我上面的例子是这个的精简版)


小智 5

你可以试试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;
Run Code Online (Sandbox Code Playgroud)

支持:

  • UAC窗口
  • Winlogon中
  • DirectShowOverlay

  • @shekh:你的研究太肤浅了。该代码使用 IDirectDrawSurface7-&gt;BltFast() 将屏幕从屏幕绘图表面复制到复制 DD 表面,然后使用 Filemapping 复制图像。它非常复杂,因为代码运行在无法轻松访问桌面的服务中。 (2认同)

Kar*_*ick 5

屏幕录制可以使用VLC API在C#中完成。我已经做了一个示例程序来演示这一点。它使用LibVLCSharpVideoLAN.LibVLC.Windows库。您可以使用此跨平台 API 实现更多与视频渲染相关的功能。

有关 API 文档,请参阅:LibVLCSharp API Github

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using LibVLCSharp.Shared;

namespace ScreenRecorderNetApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Core.Initialize();

            using (var libVlc = new LibVLC())
            using (var mediaPlayer = new MediaPlayer(libVlc))
            {
                var media = new Media(libVlc, "screen://", FromType.FromLocation);
                media.AddOption(":screen-fps=24");
                media.AddOption(":sout=#transcode{vcodec=h264,vb=0,scale=0,acodec=mp4a,ab=128,channels=2,samplerate=44100}:file{dst=testvlc.mp4}");
                media.AddOption(":sout-keep");

                mediaPlayer.Play(media);
                Thread.Sleep(10*1000);
                mediaPlayer.Stop();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不太准确,LibVLC 是 LGPL - 它于 2011 年重新获得许可。VLC 应用程序本身仍然是 GPL:https://www.videolan.org/press/lgpl-libvlc.html (2认同)