简体   繁体   English

获取 EGL DRM/KMS 应用程序的屏幕截图

[英]Get screenshot of EGL DRM/KMS application

How to get screenshot of graphical application programmatically?如何以编程方式获取图形应用程序的屏幕截图? Application draw its window using EGL API via DRM/KMS.应用程序通过 DRM/KMS 使用 EGL API 绘制其窗口。

I use Ubuntu Server 16.04.3 and graphical application written using Qt 5.9.2 with EGLFS QPA backend.我使用 Ubuntu Server 16.04.3 和使用 Qt 5.9.2 编写的图形应用程序和 EGLFS QPA 后端。 It started from first virtual terminal (if matters), then it switch display to output in full HD graphical mode.它从第一个虚拟终端(如果重要)开始,然后以全高清图形模式将显示切换到输出。

When I use utilities (eg fb2png ) which operates on /dev/fb?当我使用在/dev/fb?上运行的实用程序(例如fb2png )时/dev/fb? , then only textmode contents of first virtual terminal ( Ctrl+Alt+F1 ) are saved as screenshot. ,然后只有第一个虚拟终端( Ctrl+Alt+F1 )的文本模式内容被保存为屏幕截图。

It is hardly, that there are EGL API to get contents of any buffer from context of another process (it would be insecure), but maybe there are some mechanism (and library) to get access to final output of GPU?几乎没有 EGL API 可以从另一个进程的上下文中获取任何缓冲区的内容(这将是不安全的),但是也许有某种机制(和库)可以访问 GPU 的最终输出?

  1. One way would be to get a screenshot from within your application, reading the contents of the back buffer with glReadPixels() .一种方法是从应用程序中获取屏幕截图,使用glReadPixels()读取后台缓冲区的内容。 Or use QQuickWindow::grabWindow() , which internally uses glReadPixels() in the correct way.或者使用QQuickWindow::grabWindow() ,它在内部以正确的方式使用glReadPixels() This seems to be not an option for you, as you need to take a screenshot when the Qt app is frozen.这似乎不是您的选择,因为您需要在 Qt 应用程序冻结时截取屏幕截图。

  2. The other way would be to use the DRM API to map the framebuffer and then memcpy the mapped pixels.另一种方法是使用 DRM API 来映射帧缓冲区,然后memcpy映射的像素。 This is implemented in Chromium OS with Python and can be translated to C easily, see https://chromium-review.googlesource.com/c/chromiumos/platform/factory/+/367611 .这是在 Chromium OS 中使用 Python 实现的,可以轻松转换为 C,请参阅https://chromium-review.googlesource.com/c/chromiumos/platform/factory/+/367611 The DRM API can also be used by another process than the Qt UI process that does the rendering.除了进行渲染的 Qt UI 进程之外,其他进程也可以使用 DRM API。

This is a very interesting question, and I have fought this problem from several angles.这是一个非常有趣的问题,我从多个角度解决了这个问题。

The problem is quite complex and dependant on platform, you seem to be running on EGL, which means embedded, and there you have few options unless your platform offers them.问题非常复杂并且取决于平台,您似乎在 EGL 上运行,这意味着嵌入式,除非您的平台提供,否则您几乎没有选择。

The options you have are:您拥有的选项是:

glTexSubImage2D glTexSubImage2D

glTexSubImage2D can copy several kinds of buffers from OpenGL textures to CPU memory. glTexSubImage2D可以将多种缓冲区从 OpenGL 纹理复制到 CPU 内存。 Unfortunatly it is not supported in GLES 2/3, but your embedded provider might support it via an extension.不幸的是,GLES 2/3 不支持它,您的嵌入式提供商可能通过扩展支持它。 This is nice because you can either render to FBO or get the pixels from the specific texture you need.这很好,因为您可以渲染到 FBO 或从您需要的特定纹理中获取像素。 It also needs minimal code intervertion.它还需要最少的代码交互。

glReadPixels读像素数

glReadPixels is the most common way to download all or part of the GPU pixels which are already rendered. glReadPixels是下载已渲染的全部或部分 GPU 像素的最常用方法。 Albeit slow, it works on GLES and Desktop.尽管速度很慢,但它适用于 GLES 和桌面。 On Desktop with a decent GPU is bearable up to interactive framerates, but beware on embedded it might be really slow as it stops your render thread to get the data (horrible framedrops ensured).在具有不错 GPU 的台式机上可以承受交互式帧速率,但要注意嵌入式它可能会慢,因为它会停止渲染线程以获取数据(确保可怕的掉帧)。 You can save code as it can be made to work with minimal code modifications.您可以保存代码,因为只需修改最少的代码即可使其工作。

Pixel Buffer Objects (PBO's)像素缓冲区对象 (PBO)

Once you start doing real research PBO's appear here and there because they can be made to work asynchronously.一旦您开始进行真正的研究, PBO就会到处出现,因为它们可以异步工作。 They are also generally not supported in embedded but can work really well on desktop even on mediocre GPU's.它们通常也不受嵌入式支持,但即使在平庸的 GPU 上也可以在桌面上很好地工作。 Also a bit tricky to setup and require specific render modifications.设置也有点棘手,需要特定的渲染修改。

Framebuffer帧缓冲区

On embedded, sometimes you already render to the framebuffer, so go there and fetch the pixels.在嵌入时,有时你已经渲染到帧缓冲区,所以去那里获取像素。 Also works on desktop.也适用于桌面。 You can enven mmap() the buffer to a file and get partial contents easily.您可以将mmap()缓冲区封装到文件中并轻松获取部分内容。 But beware in many embedded systems EGL does not work on the framebuffer but on a different 'overlay' so you might be snapshotting the background of it.但要注意,在许多嵌入式系统中,EGL 不适用于帧缓冲区,而是在不同的“覆盖”上工作,因此您可能会对其背景进行快照。 Also to note some multimedia applications are run with UI's on the EGL and media players on the framebuffer.还要注意一些多媒体应用程序在 EGL 上运行 UI,在帧缓冲区上运行媒体播放器。 So if you only need to capture the video players this might work for you.因此,如果您只需要捕获视频播放器,这可能适合您。 In other cases there is EGL targeting a texture which is copied to the framebuffer, and it will also work just fine.在其他情况下,有 EGL 目标是复制到帧缓冲区的纹理,它也可以正常工作。

As far as I know render to texture and stream to a framebuffer is the way they made the sweet Qt UI you see on the Ableton Push 2据我所知渲染到纹理和流到帧缓冲区是他们制作你在Ableton Push 2上看到的甜美 Qt UI 的方式在此处输入图片说明

More exotic Dispmanx/OpenWF更多异国情调的 Dispmanx/OpenWF

On some embedded systems (notably the Raspberry Pi and most Broadcom Videocore's) you have DispmanX.在某些嵌入式系统(特别是 Raspberry Pi 和大多数 Broadcom Videocore 的)上,您有 DispmanX。 Whichs is really interesting :这真的很有趣

This is fun:这很有趣:

The lowest level of accessing the GPU seems to be by an API called Dispmanx[...]访问 GPU 的最低级别似乎是通过名为 Dispmanx[...] 的 API

It continues...它继续...

Just to give you total lack of encouragement from using Dispmanx there are hardly any examples and no serious documentation.只是为了让您完全缺乏使用 Dispmanx 的鼓励,几乎没有任何示例,也没有严肃的文档。

Basically DispmanX is very near to baremetal.基本上 DispmanX 非常接近裸机。 So it is even deeper down than the framebuffer or EGL.所以它比帧缓冲区或 EGL 更深。 Really interesting stuff because you can use vc_dispmanx_snapshot() and really get a snapshot of everything really fast.非常有趣的东西,因为您可以使用vc_dispmanx_snapshot()并真正快速地获得所有内容的快照。 And by fast I mean I got 30FPS RGBA32 screen capture with no noticeable stutter on screen and about 4~6% of extra CPU overhead on a Rasberry Pi.快速我的意思是我获得了 30FPS RGBA32 屏幕捕获,屏幕上没有明显的卡顿,并且在 Rasberry Pi 上有大约 4% 到 6% 的额外 CPU 开销。 Night and day because glReadPixels got was producing very noticeable framedrops even for 1x1 pixel capture.即使是 1x1 像素捕获,由于 glReadPixels 得到的日夜都会产生非常明显的掉帧。

That's pretty much what I've found.这几乎就是我发现的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM