【问题标题】:Get screenshot of EGL DRM/KMS application获取 EGL DRM/KMS 应用程序的屏幕截图
【发布时间】:2018-05-05 20:00:26
【问题描述】:

如何以编程方式获取图形应用程序的屏幕截图?应用程序通过 DRM/KMS 使用 EGL API 绘制其窗口。

我使用 Ubuntu Server 16.04.3 和使用 Qt 5.9.2 和 EGLFS QPA 后端编写的图形应用程序。它从第一个虚拟终端(如果有的话)开始,然后将显示切换到全高清图形模式输出。

当我使用在/dev/fb? 上运行的实用程序(例如fb2png)时,只有第一个虚拟终端(Ctrl+Alt+F1)的文本模式内容被保存为屏幕截图。

几乎没有 EGL API 可以从另一个进程的上下文中获取任何缓冲区的内容(这将是不安全的),但也许有一些机制(和库)可以访问 GPU 的最终输出?

【问题讨论】:

  • 也许可以创建一个 shim 库来转发 EGL 调用并在启动应用程序时使用 LD_PRELOAD 加载它。
  • @Velkan 有趣的想法,谢谢。即使图形应用程序的进程没有响应,我也需要查看屏幕。

标签: linux qt ubuntu opengl egl


【解决方案1】:
  1. 一种方法是从应用程序中获取屏幕截图,使用glReadPixels() 读取后台缓冲区的内容。或者使用QQuickWindow::grabWindow(),它在内部以正确的方式使用glReadPixels()。这似乎不适合您,因为您需要在 Qt 应用程序冻结时截屏。

  2. 另一种方法是使用 DRM API 映射帧缓冲区,然后 memcpy 映射像素。这是在 Chromium OS 中使用 Python 实现的,可以很容易地转换为 C,请参阅https://chromium-review.googlesource.com/c/chromiumos/platform/factory/+/367611。除了执行渲染的 Qt UI 进程之外,其他进程也可以使用 DRM API。

【讨论】:

    【解决方案2】:

    这是一个非常有趣的问题,我从多个角度解决了这个问题。

    这个问题相当复杂并且依赖于平台,你似乎在 EGL 上运行,这意味着嵌入式,除非你的平台提供它们,否则你几乎没有选择。

    您有以下选择:

    glTexSubImage2D

    glTexSubImage2D 可以将多种缓冲区从 OpenGL 纹理复制到 CPU 内存。不幸的是,GLES 2/3 不支持它,您的嵌入式提供商可能会通过扩展来支持它。这很好,因为您可以渲染到 FBO 或从您需要的特定纹理中获取像素。它还需要最少的代码转换。

    glReadPixels

    glReadPixels 是下载已渲染的全部或部分 GPU 像素的最常用方法。尽管速度很慢,但它可以在 GLES 和桌面上运行。在具有不错 GPU 的桌面上可以承受交互式帧速率,但要注意嵌入式它可能会真的很慢,因为它会停止渲染线程来获取数据(确保可怕的丢帧)。您可以保存代码,因为它可以通过最少的代码修改来工作。

    像素缓冲区对象 (PBO)

    一旦您开始进行真正的研究,PBO's 就会出现在这里和那里,因为它们可以异步工作。它们通常也不支持嵌入式,但即使在平庸的 GPU 上也可以在桌面上很好地工作。设置起来也有点棘手,需要特定的渲染修改。

    帧缓冲

    在嵌入时,有时您已经渲染到帧缓冲区,所以去那里获取像素。也适用于桌面。您可以将mmap() 缓存到文件中并轻松获取部分内容。但要注意,在许多嵌入式系统中,EGL 不能在帧缓冲区上工作,而是在不同的“覆盖”上工作,因此您可能正在对它的背景进行快照。还要注意,一些多媒体应用程序在 EGL 上使用 UI 运行,在帧缓冲区上使用媒体播放器。因此,如果您只需要捕获视频播放器,这可能对您有用。在其他情况下,EGL 针对复制到帧缓冲区的纹理,它也可以正常工作。

    据我所知,渲染到纹理并流式传输到帧缓冲区是他们制作您在Ableton Push 2 上看到的甜蜜 Qt UI 的方式

    更奇特的 Dispmanx/OpenWF

    在某些嵌入式系统(尤其是 Raspberry Pi 和大多数 Broadcom Videocore 的)上,您有 DispmanX。 Whichs is really interesting:

    这很有趣:

    访问 GPU 的最低级别似乎是通过名为 Dispmanx[...] 的 API 进行的

    继续……

    只是为了让您完全没有使用 Dispmanx 的鼓励,几乎没有任何示例,也没有严肃的文档。

    基本上 DispmanX 非常接近裸机。所以它比帧缓冲区或 EGL 更深。非常有趣的东西,因为您可以使用vc_dispmanx_snapshot() 并真正快速地获得所有内容的快照。快速我的意思是我获得了 30FPS RGBA32 屏幕捕获,屏幕上没有明显的卡顿,并且在 Rasberry Pi 上大约 4~6% 的额外 CPU 开销。日夜不停,因为 glReadPixels 得到的即使是 1x1 像素捕获也会产生非常明显的丢帧。

    这几乎就是我发现的。

    【讨论】:

      猜你喜欢
      • 2010-10-27
      • 1970-01-01
      • 2010-12-03
      • 2013-11-26
      • 1970-01-01
      • 2011-11-04
      • 2017-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多