diff --git a/tools/chatgui/src/stream_publish_engine.cpp b/tools/chatgui/src/stream_publish_engine.cpp index d517c02f..2d4002d0 100644 --- a/tools/chatgui/src/stream_publish_engine.cpp +++ b/tools/chatgui/src/stream_publish_engine.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,7 @@ struct StreamPublishEngine::Impl { std::unique_ptr sink; std::unique_ptr camera; std::unique_ptr screen; + std::unique_ptr screenGrabTimer; std::unique_ptr 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(); + 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(); m->screenGrabTimer->setTimerType(Qt::PreciseTimer); + connect(m->screenGrabTimer.get(), &QTimer::timeout, this, [this, generation, screen = QPointer(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(); 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(); 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; }