获取 EGL DRM/KMS 应用程序的屏幕截图
Get screenshot of EGL DRM/KMS application
如何以编程方式获取图形应用程序的屏幕截图?应用程序通过 DRM/KMS.
使用 EGL API 绘制其 window
我使用 Ubuntu 服务器 16.04.3 和使用带有 EGLFS QPA 后端的 Qt 5.9.2 编写的图形应用程序。它从第一个虚拟终端开始(如果重要的话),然后将显示切换为全高清图形模式输出。
当我使用在 /dev/fb?
上运行的实用程序(例如 fb2png
)时,只有第一个虚拟终端的文本模式内容(Ctrl+Alt+F1 ) 保存为屏幕截图。
几乎没有 EGL API 从另一个进程的上下文中获取任何缓冲区的内容(这将是不安全的),但也许有一些机制(和库)可以访问GPU 的最终输出?
一种方法是从您的应用程序中获取屏幕截图,使用 glReadPixels()
读取后台缓冲区的内容。或者使用 QQuickWindow::grabWindow(),它在内部以正确的方式使用 glReadPixels()
。这似乎不适合您,因为您需要在 Qt 应用程序冻结时截取屏幕截图。
另一种方法是使用 DRM API 映射帧缓冲区,然后 memcpy
映射像素。这是在 Chromium OS 和 Python 中实现的,可以轻松转换为 C,请参阅 https://chromium-review.googlesource.com/c/chromiumos/platform/factory/+/367611。除了执行渲染的 Qt UI 进程之外,DRM API 也可以被其他进程使用。
这是个很有意思的问题,我也从几个角度来解决过这个问题。
问题非常复杂并且取决于平台,您似乎运行正在使用 EGL,这意味着嵌入式,除非您的平台提供它们,否则您几乎没有选择。
您的选择是:
glTexSubImage2D
glTexSubImage2D 可以将几种缓冲区从 OpenGL 纹理复制到 CPU 内存。不幸的是,它在 GLES 2/3 中不受支持,但是您的嵌入式提供商可能会通过扩展支持它。这很好,因为您可以渲染到 FBO 或从您需要的特定纹理中获取像素。它还需要最少的代码干预。
glReadPixels
glReadPixels 是下载已渲染的全部或部分 GPU 像素的最常用方法。虽然速度很慢,但它适用于 GLES 和桌面。在具有良好 GPU 的桌面上可以承受交互式帧率,但要注意嵌入式它可能 真的 慢,因为它会停止渲染线程以获取数据(确保可怕的帧丢失)。您可以保存代码,因为它可以通过最少的代码修改来工作。
像素缓冲区对象 (PBO)
一旦您开始进行真正的研究,PBO's 就会到处出现,因为它们可以异步工作。它们在嵌入式中通常也不被支持,但即使在普通的 GPU 上也可以在桌面上很好地工作。设置也有点棘手,需要特定的渲染修改。
帧缓冲区
在嵌入式上,有时您已经渲染到帧缓冲区,所以去那里获取像素。也适用于桌面。您可以将 mmap()
缓冲区写入文件并轻松获取部分内容。但要注意,在许多嵌入式系统中,EGL 不能在帧缓冲区上工作,而是在不同的 'overlay' 上工作,因此您可能正在对其背景进行快照。还要注意一些多媒体应用程序是 运行,在 EGL 上是 UI,在帧缓冲区上是媒体播放器。因此,如果您只需要捕获视频播放器,这可能适合您。在其他情况下,有 EGL 以复制到帧缓冲区的纹理为目标,它也可以正常工作。
据我所知,渲染到纹理并流式传输到帧缓冲区是他们制作您在 Ableton Push 2 上看到的甜蜜 Qt UI 的方式
更具异域风情Dispmanx/OpenWF
在某些嵌入式系统(特别是 Raspberry Pi 和大多数 Broadcom Videocore 的)上,您有 DispmanX。 Whichs is really interesting:
这很有趣:
The lowest level of accessing the GPU seems to be by an API called Dispmanx[...]
继续...
Just to give you total lack of encouragement from using Dispmanx there are hardly any examples and no serious documentation.
基本上 DispmanX 非常接近裸机。所以它甚至比帧缓冲区或 EGL 更深。非常有趣的东西,因为您可以使用 vc_dispmanx_snapshot()
并真正快速地获取所有内容的快照。快速我的意思是我获得了 30FPS RGBA32 屏幕捕获,屏幕上没有明显的卡顿,并且在 Rasberry Pi 上有大约 4~6% 的额外 CPU 开销。因为 glReadPixels 日以继夜地产生了非常明显的帧丢失,即使对于 1x1 像素捕获也是如此。
这就是我所发现的。
如何以编程方式获取图形应用程序的屏幕截图?应用程序通过 DRM/KMS.
使用 EGL API 绘制其 window我使用 Ubuntu 服务器 16.04.3 和使用带有 EGLFS QPA 后端的 Qt 5.9.2 编写的图形应用程序。它从第一个虚拟终端开始(如果重要的话),然后将显示切换为全高清图形模式输出。
当我使用在 /dev/fb?
上运行的实用程序(例如 fb2png
)时,只有第一个虚拟终端的文本模式内容(Ctrl+Alt+F1 ) 保存为屏幕截图。
几乎没有 EGL API 从另一个进程的上下文中获取任何缓冲区的内容(这将是不安全的),但也许有一些机制(和库)可以访问GPU 的最终输出?
一种方法是从您的应用程序中获取屏幕截图,使用
glReadPixels()
读取后台缓冲区的内容。或者使用 QQuickWindow::grabWindow(),它在内部以正确的方式使用glReadPixels()
。这似乎不适合您,因为您需要在 Qt 应用程序冻结时截取屏幕截图。另一种方法是使用 DRM API 映射帧缓冲区,然后
memcpy
映射像素。这是在 Chromium OS 和 Python 中实现的,可以轻松转换为 C,请参阅 https://chromium-review.googlesource.com/c/chromiumos/platform/factory/+/367611。除了执行渲染的 Qt UI 进程之外,DRM API 也可以被其他进程使用。
这是个很有意思的问题,我也从几个角度来解决过这个问题。
问题非常复杂并且取决于平台,您似乎运行正在使用 EGL,这意味着嵌入式,除非您的平台提供它们,否则您几乎没有选择。
您的选择是:
glTexSubImage2D
glTexSubImage2D 可以将几种缓冲区从 OpenGL 纹理复制到 CPU 内存。不幸的是,它在 GLES 2/3 中不受支持,但是您的嵌入式提供商可能会通过扩展支持它。这很好,因为您可以渲染到 FBO 或从您需要的特定纹理中获取像素。它还需要最少的代码干预。
glReadPixels
glReadPixels 是下载已渲染的全部或部分 GPU 像素的最常用方法。虽然速度很慢,但它适用于 GLES 和桌面。在具有良好 GPU 的桌面上可以承受交互式帧率,但要注意嵌入式它可能 真的 慢,因为它会停止渲染线程以获取数据(确保可怕的帧丢失)。您可以保存代码,因为它可以通过最少的代码修改来工作。
像素缓冲区对象 (PBO)
一旦您开始进行真正的研究,PBO's 就会到处出现,因为它们可以异步工作。它们在嵌入式中通常也不被支持,但即使在普通的 GPU 上也可以在桌面上很好地工作。设置也有点棘手,需要特定的渲染修改。
帧缓冲区
在嵌入式上,有时您已经渲染到帧缓冲区,所以去那里获取像素。也适用于桌面。您可以将 mmap()
缓冲区写入文件并轻松获取部分内容。但要注意,在许多嵌入式系统中,EGL 不能在帧缓冲区上工作,而是在不同的 'overlay' 上工作,因此您可能正在对其背景进行快照。还要注意一些多媒体应用程序是 运行,在 EGL 上是 UI,在帧缓冲区上是媒体播放器。因此,如果您只需要捕获视频播放器,这可能适合您。在其他情况下,有 EGL 以复制到帧缓冲区的纹理为目标,它也可以正常工作。
据我所知,渲染到纹理并流式传输到帧缓冲区是他们制作您在 Ableton Push 2 上看到的甜蜜 Qt UI 的方式
更具异域风情Dispmanx/OpenWF
在某些嵌入式系统(特别是 Raspberry Pi 和大多数 Broadcom Videocore 的)上,您有 DispmanX。 Whichs is really interesting:
这很有趣:
The lowest level of accessing the GPU seems to be by an API called Dispmanx[...]
继续...
Just to give you total lack of encouragement from using Dispmanx there are hardly any examples and no serious documentation.
基本上 DispmanX 非常接近裸机。所以它甚至比帧缓冲区或 EGL 更深。非常有趣的东西,因为您可以使用 vc_dispmanx_snapshot()
并真正快速地获取所有内容的快照。快速我的意思是我获得了 30FPS RGBA32 屏幕捕获,屏幕上没有明显的卡顿,并且在 Rasberry Pi 上有大约 4~6% 的额外 CPU 开销。因为 glReadPixels 日以继夜地产生了非常明显的帧丢失,即使对于 1x1 像素捕获也是如此。
这就是我所发现的。