假设我有一个全屏运行的OpenGL游戏(如《求生之路2》)。我想通过编程方式获得它的屏幕截图,然后将其写入视频文件。
我尝试过GDI、D3D和OpenGL方法(例如glReadPixels),但要么收到空白屏幕,要么在捕获流中出现闪烁。
有什么建议吗?
值得一提的是,类似于我正在尝试实现的东西的范例是Fraps。
假设我有一个全屏运行的OpenGL游戏(如《求生之路2》)。我想通过编程方式获得它的屏幕截图,然后将其写入视频文件。
我尝试过GDI、D3D和OpenGL方法(例如glReadPixels),但要么收到空白屏幕,要么在捕获流中出现闪烁。
有什么建议吗?
值得一提的是,类似于我正在尝试实现的东西的范例是Fraps。
这个问题有几种解决方法。其中大部分都不太好,完全取决于你想要针对哪种图形API以及目标应用程序使用哪些函数。 大多数DirectX、GDI+和OpenGL应用程序都是双缓冲或三重缓冲的,因此它们都会调用:
void SwapBuffers(HDC hdc)
在某个时刻,它们还会在消息队列中生成WM_PAINT消息,每当窗口需要绘制时。这给了你两个选择。
你可以安装全局钩子或线程本地钩子到目标进程中并捕获WM_PAINT消息。这允许你在绘画发生之前从设备上下文中复制内容。可以通过枚举系统上的所有进程并查找已知的窗口名称或已知的模块句柄来找到该进程。
你可以将代码注入到目标进程的SwapBuffers的本地副本中。在Linux上,可以通过LD_PRELOAD环境变量或显式调用ld-linux.so.2轻松完成此操作,但在Windows上没有相应的方法。幸运的是,微软研究部门有一个名为Detours的框架可以为您完成此操作。你可以在这里找到它:link。
Farbrausch是一个demoscene小组,他们开发了一个名为kkapture的演示捕获工具,该工具利用了Detours库。他们的工具针对不需要用户输入的应用程序,因此他们通过挂钩所有可能的时间函数(如timeGetTime()、GetTickCount()和QueryPerformanceCounter())以固定帧速率运行演示。这真是太酷了。关于kkapture内部的演示,ryg(我想是他)写了一篇演示,可以在这里找到。我认为这对你很有兴趣。
有关Windows hooks的更多信息,请参见这里和这里。
这个想法让我很感兴趣,所以我使用Detours来挂钩OpenGL应用程序并操纵图形。这是添加了绿色雾的Quake 2:
Detours在两个层面上工作。实际的hooking只在与目标进程相同的进程空间中起作用。因此,Detours有一个函数可以将DLL注入到进程中并强制运行其DLLMain,以及应在该DLL中使用的函数。当运行DLLMain时,DLL应调用DetourAttach()来指定要挂钩的函数,以及“detour”函数,即您想要覆盖的代码。
所以它基本上是这样工作的:
然而,有一些注意事项。当运行DllMain时,稍后使用LoadLibrary()导入的库尚不可见。因此,在DLL附加事件期间不能必然设置所有内容。解决方法是跟踪到目前为止被覆盖的所有函数,并尝试在您已经可以调用的这些函数内初始化其他函数。这样,您将会在LoadLibrary将它们映射到进程的内存空间中时立即发现新的函数。但我不确定这对于wglGetProcAddress是否有效。(也许这里还有其他人有关于此的想法?)
一些LoadLibrary()调用似乎会失败。我测试了Quake 2,DirectSound和waveOut API由于某种原因无法初始化。我仍在调查此问题。