Мультимедиа

Захват экрана и трансляция (screen capture)

Введение

Помимо записи с камеры, иногда нужно записывать или транслировать содержимое самого экрана — для приложений демонстрации работы программы, записи обучающих видео, или удалённой поддержки пользователей. В статье разберём захват экрана через QScreenCapture в связке с уже знакомой архитектурой QMediaCaptureSession.

Концепция

QScreenCapture работает как источник видеоданных, аналогичный по роли QCamera, но захватывающий содержимое экрана (или окна, в зависимости от платформенной поддержки) вместо изображения с физической камеры. Он подключается к той же QMediaCaptureSession, что и обычная камера, что позволяет либо записать захват экрана в файл через QMediaRecorder, либо просто отобразить его в превью — например, для предварительного просмотра перед началом записи.

Пример кода: Захват экрана и трансляция (screen capture)

#include <QScreenCapture>
#include <QMediaCaptureSession>
#include <QMediaRecorder>
#include <QGuiApplication>
#include <QScreen>
#include <QVideoWidget>

class ScreenRecorderWidget : public QWidget
{
    Q_OBJECT
public:
    ScreenRecorderWidget(QWidget *parent = nullptr) : QWidget(parent)
    {
        m_previewWidget = new QVideoWidget(this);

        auto *layout = new QVBoxLayout(this);
        layout->addWidget(m_previewWidget);

        auto *startButton = new QPushButton("Начать запись экрана", this);
        layout->addWidget(startButton);

        // Захват основного экрана — на многомониторных системах можно выбрать конкретный QScreen
        m_screenCapture.setScreen(QGuiApplication::primaryScreen());

        m_captureSession.setScreenCapture(&m_screenCapture);
        m_captureSession.setVideoOutput(m_previewWidget);
        m_captureSession.setRecorder(&m_recorder);

        m_screenCapture.start();

        connect(startButton, &QPushButton::clicked, this, &ScreenRecorderWidget::toggleRecording);
    }

private slots:
    void toggleRecording()
    {
        if (m_recorder.recorderState() == QMediaRecorder::RecordingState) {
            m_recorder.stop();
        } else {
            m_recorder.setOutputLocation(QUrl::fromLocalFile("/tmp/screen_recording.mp4"));
            m_recorder.record();
        }
    }

private:
    QVideoWidget *m_previewWidget;
    QScreenCapture m_screenCapture;
    QMediaCaptureSession m_captureSession;
    QMediaRecorder m_recorder;
};

Пояснения к коду

QScreenCapture::setScreen() определяет, какой именно экран захватывается — QGuiApplication::primaryScreen() выбирает основной монитор системы, но для многомониторных конфигураций можно явно передать конкретный объект QScreen из полного списка QGuiApplication::screens(), позволяя пользователю выбрать, какой именно экран записывать, если их несколько. Структура подключения к QMediaCaptureSession полностью аналогична работе с камерой — это намеренное архитектурное единообразие Qt Multimedia, упрощающее изучение модуля: освоив паттерн «источник → сессия → приёмник» для одного типа источника, тот же паттерн применяется ко всем остальным.

toggleRecording() идентичен по структуре аналогичному методу из статьи 252 про запись с камеры — что подчёркивает, что собственно запись (через QMediaRecorder) не зависит от конкретного типа источника видео, будь это камера или захват экрана.

Подводные камни

  • Захват экрана требует особых системных разрешений на некоторых платформах, в частности на современных версиях macOS, где захват экрана защищён политикой приватности на уровне ОС и требует явного разрешения пользователя через системные настройки — без выданного разрешения QScreenCapture::start() может не дать никакого изображения, и приложение должно явно проверять состояние ошибки и информировать пользователя о необходимости включить разрешение в системных настройках.
  • Запись собственного интерфейса приложения вместе с захватом экрана может создать рекурсивный визуальный эффект («зеркало в зеркале»), если окно приложения, показывающее превью захвата, само попадает в область захватываемого экрана — это особенно заметно, если окно превью не исключено явно из захвата (что не всегда технически возможно в зависимости от платформы) и может потребовать архитектурного решения вроде размещения окна предпросмотра на отдельном мониторе при многомониторной конфигурации.
  • Захват экрана с высоким разрешением (4K и выше) создаёт значительный поток данных, требующий заметных вычислительных ресурсов для кодирования в реальном времени — на менее мощном оборудовании это может привести к пропуску кадров или существенной задержке записи, и для таких сценариев может быть оправдано явное снижение разрешения записи или частоты кадров по сравнению с реальным разрешением экрана.
  • Изменение конфигурации экранов во время записи (отключение монитора, изменение разрешения, для систем с возможностью горячего подключения дисплеев) может прервать или испортить активный захват, если код не обрабатывает подобные изменения явно — для надёжного screen recording приложения стоит подписываться на сигналы изменения геометрии/доступности экранов и корректно обрабатывать такие события во время активной записи.