Browse Source

Capture the selected X11 monitor through QScreen

master
evgeny 3 days ago
parent
commit
00e9ed227a
  1. 58
      tools/chatgui/src/stream_publish_engine.cpp

58
tools/chatgui/src/stream_publish_engine.cpp

@ -7,6 +7,7 @@
#include <QScreen>
#include <QRandomGenerator>
#include <QTransform>
#include <QPixmap>
#include <QJsonArray>
#include <QJsonDocument>
#include <thread>
@ -73,6 +74,7 @@ struct StreamPublishEngine::Impl {
std::unique_ptr<QVideoSink> sink;
std::unique_ptr<QCamera> camera;
std::unique_ptr<QScreenCapture> screen;
std::unique_ptr<QTimer> screenGrabTimer;
std::unique_ptr<QWindowCapture> window;
QVideoFrame frame;
QVideoFrame previewFrame;
@ -239,21 +241,55 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
connect(m->camera.get(), &QCamera::errorOccurred, this, [failed](QCamera::Error, const QString& error) { failed(error); }, Qt::QueuedConnection);
m->camera->start();
} else if (config.source.kind == StreamVideoSource::Screen) {
m->screen = std::make_unique<QScreenCapture>();
QScreen* selected = nullptr;
if (config.source.id != "portal") {
QScreen* selected = nullptr;
for (auto* screen : QGuiApplication::screens())
if (screen == config.source.screen && screenId(screen) == config.source.id) { selected = screen; break; }
if (!selected) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop screen disappeared"); return false; }
}
// Qt X11 screen capture ищет root по имени и путает мониторы; QScreen захватывает именно выбранный экран.
if (selected && QGuiApplication::platformName() == "xcb") {
m->screenGrabTimer = std::make_unique<QTimer>(); m->screenGrabTimer->setTimerType(Qt::PreciseTimer);
connect(m->screenGrabTimer.get(), &QTimer::timeout, this, [this, generation, screen = QPointer<QScreen>(selected), failed] {
if (generation != m->sourceGeneration || !m->running) return;
int64_t captured = monotonicUs();
QImage image = screen ? screen->grabWindow(0).toImage() : QImage();
if (image.isNull()) {
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop X11 screen grab failed present=%d", !screen.isNull());
m->screenGrabTimer->stop();
QMetaObject::invokeMethod(this, [failed] { failed(text("Video capture stopped")); }); return;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QVideoFrame frame(image);
#else
image = image.convertToFormat(QImage::Format_RGBA8888);
QVideoFrame frame(QVideoFrameFormat(image.size(), QVideoFrameFormat::Format_RGBA8888));
if (frame.map(QVideoFrame::WriteOnly)) {
for (int y = 0; y < image.height(); y++) std::memcpy(frame.bits(0) + y * frame.bytesPerLine(0), image.constScanLine(y), image.width() * 4);
frame.unmap();
} else frame = {};
#endif
if (!frame.isValid()) {
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop X11 video frame allocation failed size=%dx%d", image.width(), image.height());
m->screenGrabTimer->stop();
QMetaObject::invokeMethod(this, [failed] { failed(text("Video frame conversion failed")); }); return;
}
frame.setStartTime(captured); m->sink->setVideoFrame(frame);
});
m->screenGrabTimer->start(std::max(1, 1000 / std::clamp(config.video.fps, 1, 60)));
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop X11 screen grab started source=%s interval_ms=%d",
qPrintable(config.source.name), m->screenGrabTimer->interval());
} else {
m->screen = std::make_unique<QScreenCapture>();
m->screen->setScreen(selected);
m->session->setScreenCapture(m->screen.get());
connect(m->screen.get(), &QScreenCapture::activeChanged, this, [this, generation](bool active) {
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop screen capture active=%d generation=%llu", active, (unsigned long long)generation);
if (!active && generation == m->sourceGeneration) sourceFailed(m->generation, text("Video capture stopped"));
}, Qt::QueuedConnection);
connect(m->screen.get(), &QScreenCapture::errorOccurred, this, [failed](QScreenCapture::Error, const QString& error) { failed(error); }, Qt::QueuedConnection);
m->screen->start();
}
m->session->setScreenCapture(m->screen.get());
connect(m->screen.get(), &QScreenCapture::activeChanged, this, [this, generation](bool active) {
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop screen capture active=%d generation=%llu", active, (unsigned long long)generation);
if (!active && generation == m->sourceGeneration) sourceFailed(m->generation, text("Video capture stopped"));
}, Qt::QueuedConnection);
connect(m->screen.get(), &QScreenCapture::errorOccurred, this, [failed](QScreenCapture::Error, const QString& error) { failed(error); }, Qt::QueuedConnection);
m->screen->start();
} else {
auto window = config.source.window.value<QCapturableWindow>();
if (!window.isValid()) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop window is unavailable"); return false; }
@ -274,6 +310,7 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
}
void StreamPublishEngine::closeSource() {
#ifdef HAVE_STREAM_CAPTURE
m->screenGrabTimer.reset();
emit previewVideoChanged(QVariant());
if (m->sink) disconnect(m->sink.get(), nullptr, this, nullptr);
if (m->camera) { disconnect(m->camera.get(), nullptr, this, nullptr); m->camera->stop(); }
@ -331,6 +368,9 @@ bool StreamPublishEngine::reconfigure(const StreamPublishConfig& config) {
#endif
}
if (sourceChange && !openSource(config)) { sourceFailed(m->generation, text("Could not open the selected video source")); return false; }
#ifdef HAVE_STREAM_CAPTURE
if (m->screenGrabTimer) m->screenGrabTimer->setInterval(std::max(1, 1000 / std::clamp(config.video.fps, 1, 60)));
#endif
m->wake.notify_all(); emit stateChanged(text("Applying video settings…")); return true;
}

Loading…
Cancel
Save