完整的 Windows 桌面的实时视频处理

Buz*_*zby 5 c# c++ windows

我想为完整的 Windows 桌面添加动态视频效果。

我希望能够将屏幕变成灰色,斜切边缘并添加一些扫描线,使其看起来像一个旧的 CRT 屏幕,或者使屏幕出现故障,就像他们在不懂技术的电影中显示被黑系统一样,等等。

效果本身超出了这个问题的范围。这个问题是如何应用它们,即如何获取Windows 生成的桌面图像,应用我的效果,并在同一显示器上呈现结果。

我知道几种可能有效的方法。

  1. 连接到绘制东西的 WinAPI 调用。
  2. 创建一个虚假的辅助显示器,使其成为主要显示设备,从中获取视频流,应用我的效果,并在真实显示器上呈现
  3. 创建应用效果的自定义显示驱动程序。

它们都有缺点:复杂性、驱动程序签名要求、复杂的设置。有什么更好的方法来实现我想要的吗?

Soo*_*nts 5

1 非常难,不同的 API 太多。

\n\n

2 困难的部分是制作假显示器。如果您\xe2\x80\x99ll 购买一个名为 \xe2\x80\x9cHDMI 虚拟插头\xe2\x80\x9d 的 10 美元设备,它将变得相对简单,具有 100% 记录的 API。使用桌面复制 API 获取显示器 1 的纹理,应用您想要的任何效果并在显示器 2 上显示。如果您想要良好的性能,您最好完全在 GPU 上实现处理,例如使用自定义像素着色器渲染四边形。

\n\n

3 可以,但是很难做到。

\n\n

还有\xe2\x80\x99s另一种方式。它\xe2\x80\x99的实现很棘手,并且使用未记录的API,但根据我的经验相当可靠,至少在Windows 7上是如此。编写一个将自身注入到dwm.exe中的DLL。\xe2\x80\x99 是一个 Windows 进程 \xe2\x80\x9c 桌面窗口管理器\xe2\x80\x9d,它组成了桌面上可见的任何内容。DLL注入后,创建一个新线程,在该线程中调用D3D11CreateDeviceAndSwapChain,然后使用例如MinHook来拦截Present,并且最好还拦截接口ResizeBuffers的方法IDXGISwapChain。如果成功,dwm.exe 将在每次呈现框架或桌面分辨率更改时调用 DLL 中的函数。然后,在当前的函数中,您可以做您的事情,例如添加另一个渲染通道来实现您的效果,然后调用原始实现以实际将结果呈现到桌面。

\n\n

这在理论上很容易,但在实践中实施起来却相当棘手。例如\xe2\x80\x99很难调试dwm.exe,你\xe2\x80\x99必须依赖日志记录,或者可能使用带有远程调试器的虚拟机。此外,这也不能跨 Windows 版本移植。另一个限制是,它不适用于视频游戏等全屏应用程序,它们会绕过 dwm.exe。对于视频游戏,仅适用于 \xe2\x80\x9c 无边框全屏窗口\xe2\x80\x9d 游戏内设置。

\n\n

更新:另一种方法,更简单。创建一个具有每像素透明度的最顶层全屏窗口。操作系统支持它们数十年,设置WS_EX_LAYEREDWS_EX_TRANSPARENT扩展样式位。你将无法进行灰度处理,因为你只能覆盖你的东西,而不能读取下面的内容,但边缘、扫描线和毛刺是完全可行的。为了获得最佳性能,请使用一些以 GPU 为中心的 API 来渲染该窗口,例如 C++ 中的 Direct2D 或 D3D。\xe2\x80\x99s 也更容易调试,可以使用 2 个显示器,也可以定位透明窗口,使其占据角落的一个矩形,为 IDE 留下足够的屏幕空间。这是一个例子(不是我的)。

\n

  • @Rob 发布在元上讨论 https://meta.stackoverflow.com/questions/388073/tag-reputation-for-close-voting 欢迎您在那里解释您的决定。 (2认同)

Ser*_*lis 5

要在窗口上捕获和重绘桌面,您不能错过方便的窗口放大工具。
您想要创建的是它的全屏版本,并进行自定义处理以使其看起来像您想要的那样。

幸运的是,有一个 GitHub包含它的工作源代码

您会注意到全屏文件中有一个函数:

//
// FUNCTION: SetColorGrayscaleState()
//
// PURPOSE: Either apply grayscale to all colors on the screen, or restore the original colors.
//
void SetColorGrayscaleState(_In_ BOOL fGrayscaleOn)
{
    // Apply the color matrix required to either invert the screen colors or to show the regular colors.
    PMAGCOLOREFFECT pEffect = (fGrayscaleOn ? &g_MagEffectGrayscale : &g_MagEffectIdentity);

    MagSetFullscreenColorEffect(pEffect);
}
Run Code Online (Sandbox Code Playgroud)

这为您解决如何影响绘制回窗口的像素提供了相当坚实的基础。

要使此窗口可点击,您需要使用类似以下代码行的内容来创建它:

HWND hWnd = CreateWindowEx(WS_EX_LAYERED|WS_EX_TRANSPARENT, cName, wTitle, NULL, 0, 0, 640, 480, NULL, 0, GetModuleHandle(NULL), 0);
Run Code Online (Sandbox Code Playgroud)

这里重要的部分是WS_EX_LAYERED|WS_EX_TRANSPARENT标志组合。

来自微软文档

不重要。但是,如果分层窗口具有 WS_EX_TRANSPARENT 扩展窗口样式,则分层窗口的形状将被忽略,并且鼠标事件将传递到分层窗口下方的其他窗口。

有了这个基础,以及如何构建点击窗口的知识,您应该能够编写一个程序来更改颜色/向桌面前面的全屏窗口添加一些工件。

我用这种方式制作的所有程序的 FPS 都比您的本机桌面低,但对于一个看起来很酷的程序来说应该还是不错的。

  • 另外,应该注意的是,这需要应用程序以管理员身份运行(出于明显的原因)。 (2认同)