Capture the selected X11 monitor through QScreen
This commit is contained in:
1 parent
ac8e701406
commit
00e9ed227a
1 file changed
+50
-10
@@ -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; }
|
||||
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();
|
||||
// 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();
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user