Получите скриншот приложения EGL DRM/KMS

Как программно получить скриншот графического приложения? Приложение рисует свое окно с помощью EGL API через DRM/KMS.

Я использую Ubuntu Server 16.04.3 и графическое приложение, написанное с использованием Qt 5.9.2 с бэкэндом EGLFS QPA. Он запускается с первого виртуального терминала (если имеет значение), затем переключает отображение на вывод в графическом режиме Full HD.

Когда я использую утилиты (например, fb2png), которые работают с /dev/fb?, то в качестве скриншота сохраняется только текстовое содержимое первого виртуального терминала (Ctrl+Alt+F1).

Вряд ли есть EGL API для получения содержимого любого буфера из контекста другого процесса (это было бы небезопасно), но, может быть, есть какой-то механизм (и библиотека) для получения доступа к конечному результату GPU?


person Tomilov Anatoliy    schedule 22.11.2017    source источник
comment
Может быть, сделать библиотеку прокладок, которая перенаправляет вызовы EGL, и загружать ее с помощью LD_PRELOAD при запуске приложения.   -  person Velkan    schedule 22.11.2017
comment
@Velkan Интересная идея, спасибо. Мне нужно видеть экран, даже если процесс графического приложения не отвечает.   -  person Tomilov Anatoliy    schedule 22.11.2017


Ответы (2)


  1. Одним из способов было бы получить снимок экрана из вашего приложения, прочитав содержимое заднего буфера с помощью glReadPixels(). Или используйте QQuickWindow::grabWindow(), который внутренне использует glReadPixels() в правильный путь. Кажется, это не вариант для вас, так как вам нужно сделать снимок экрана, когда приложение Qt зависло.

  2. Другой способ — использовать DRM API для сопоставления буфера кадра, а затем memcpy отображаемых пикселей. Это реализовано в Chromium OS с Python и может быть легко переведено на C, см. https://chromium-review.googlesource.com/c/chromiumos/platform/factory/+/367611. API DRM также может использоваться другим процессом, отличным от процесса пользовательского интерфейса Qt, который выполняет рендеринг.

person Thomas McGuire    schedule 28.02.2018

Это очень интересный вопрос, и я боролся с этой проблемой с нескольких сторон.

Проблема довольно сложная и зависит от платформы, вы, кажется, работаете на EGL, что означает встроенный, и у вас есть несколько вариантов, если только ваша платформа их не предлагает.

У вас есть следующие варианты:

glTexSubImage2D

glTexSubImage2D может копировать несколько видов буферов из текстур OpenGL. в память процессора. К сожалению, он не поддерживается в GLES 2/3, но ваш встроенный провайдер может поддерживать его через расширение. Это хорошо, потому что вы можете либо рендерить в FBO, либо получать пиксели из конкретной текстуры, которая вам нужна. Он также требует минимального вмешательства в код.

glReadPixels

glReadPixels — наиболее распространенный способ загрузки всех или часть пикселей графического процессора, которые уже обработаны. Хоть и медленно, но работает на GLES и Desktop. На настольном компьютере с приличным графическим процессором терпимо вплоть до частоты кадров в интерактивном режиме, но будьте осторожны, во встроенном режиме он может быть действительно медленным, поскольку останавливает поток рендеринга для получения данных (ужасные потери кадров обеспечены). Вы можете сохранить код, так как его можно заставить работать с минимальными изменениями кода.

Объекты пиксельного буфера (PBO)

Как только вы начнете проводить реальные исследования, -object-on-mali-t-880" rel="nofollow noreferrer">PBO появляются тут и там, потому что их можно заставить работать асинхронно. Они также, как правило, не поддерживаются во встраиваемых системах, но могут очень хорошо работать на настольных компьютерах даже на посредственных графических процессорах. Также немного сложно настроить и потребовать определенных модификаций рендеринга.

Кадровый буфер

На встроенном иногда вы уже выполняете рендеринг в фреймбуфер, поэтому идите туда и извлекайте пиксели. Также работает на рабочем столе. Вы можете mmap() преобразовать буфер в файл и легко получить частичное содержимое. Но будьте осторожны, во многих встроенных системах EGL работает не с фреймбуфером, а с другим «оверлеем», поэтому вы можете сделать снимок фона. Также следует отметить, что некоторые мультимедийные приложения запускаются с пользовательским интерфейсом в EGL и медиаплеерами в буфере кадра. Так что, если вам нужно захватить только видеоплееры, это может вам подойти. В других случаях EGL нацелен на текстуру, которая копируется во фреймбуфер, и он также будет работать нормально.

Насколько я знаю, рендеринг в текстуру и поток в фреймбуфер - это то, как они сделали приятный пользовательский интерфейс Qt, который вы видите на Ableton Push 2 введите здесь описание изображения

Более экзотический Dispmanx/OpenWF

На некоторых встроенных системах (в частности, на Raspberry Pi и большинстве Broadcom Videocore) у вас есть DispmanX. Что действительно интересно:

Это весело:

Самый низкий уровень доступа к графическому процессору, по-видимому, осуществляется через API под названием Dispmanx[...]

Это продолжается...

Просто чтобы дать вам полное отсутствие вдохновения от использования Dispmanx, почти нет примеров и серьезной документации.

В основном DispmanX очень близок к baremetal. Так что это даже глубже, чем фреймбуфер или EGL. Действительно интересный материал, потому что вы можете использовать vc_dispmanx_snapshot() и очень быстро получить моментальный снимок всего. И под быстротой я подразумеваю, что я получил снимок экрана RGBA32 со скоростью 30 кадров в секунду без заметного заикания на экране и около 4–6% дополнительных ресурсов процессора на Rasberry Pi. Ночью и днем ​​из-за glReadPixels производил очень заметные падения кадров даже при захвате 1x1 пиксель.

Это почти то, что я нашел.

person Ariel M.    schedule 12.06.2020