Browse Source

Improve Windows screen capture frame rate

stream-multiview
evgeny 1 week ago
parent
commit
bf729502a1
  1. 60
      tools/chatgui/src/stream_publish_engine.cpp

60
tools/chatgui/src/stream_publish_engine.cpp

@ -76,7 +76,7 @@ struct StreamPublishEngine::Impl {
QVideoFrame imageInput;
uint64_t imageGeneration = 0;
int64_t imagePts = -1;
bool imagePending = false, imageStop = false;
bool imagePending = false, imageStop = false, imageDrawCursor = false;
uint64_t id = 0, group = 0, generation = 0, sourceGeneration = 0, confirmedGeneration = 0, frameGeneration = 0, audioSequence = 0;
int64_t origin = 0, started = 0, framePts = -1, backendOrigin = -1, backendAnchor = -1;
int64_t audioAnchor = -1;
@ -85,6 +85,7 @@ struct StreamPublishEngine::Impl {
uint64_t capturedFrames = 0;
uint64_t convertedCaptureFrames = 0;
int64_t captureReport = 0;
uint64_t captureReportRaw = 0, captureReportConverted = 0, captureReportEncoded = 0, captureReportDelivered = 0;
std::atomic<const char*> videoStage{"stopped"};
std::atomic<int64_t> videoStageAt{0};
std::atomic<const char*> imageStage{"starting"};
@ -114,7 +115,7 @@ struct StreamPublishEngine::Impl {
#endif
std::unique_ptr<QTimer> screenGrabTimer;
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
std::unique_ptr<StreamScreenCursor> cursor;
std::shared_ptr<StreamScreenCursor> cursor;
#endif
std::unique_ptr<QWindowCapture> window;
QImage frame;
@ -326,7 +327,8 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
{
std::lock_guard<std::mutex> guard(m->lock);
if (!m->running || generation != m->sourceGeneration) return;
m->imageInput = frame; m->imageGeneration = generation; m->imagePts = framePts; m->imagePending = true;
m->imageInput = frame; m->imageGeneration = generation; m->imagePts = framePts;
m->imageDrawCursor = m->wanted.showCursor && cursorSupported(m->wanted.source); m->imagePending = true;
}
m->imageWake.notify_one();
}, Qt::DirectConnection);
@ -375,9 +377,9 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
#endif
// QScreen даёт выбранный монитор без курсора; накладываем его один раз до preview и кодирования.
// Для X11 это также исключает путаницу Qt Multimedia с одноимёнными root.
if (selected && (QGuiApplication::platformName() == "xcb" || cursorSupported(config.source))) {
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
m->cursor = std::make_unique<StreamScreenCursor>(selected);
if (selected && QGuiApplication::platformName() == "xcb") {
#if defined(UTUN_STREAM_X11_CURSOR)
{ std::lock_guard<std::mutex> guard(m->lock); m->cursor = std::make_shared<StreamScreenCursor>(selected); }
#endif
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] {
@ -417,6 +419,9 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
qPrintable(QGuiApplication::platformName()), qPrintable(config.source.name), m->screenGrabTimer->interval(),
config.showCursor, cursorSupported(config.source));
} else {
#ifdef UTUN_STREAM_WIN_CURSOR
if (selected) { std::lock_guard<std::mutex> guard(m->lock); m->cursor = std::make_shared<StreamScreenCursor>(selected); }
#endif
m->screen = std::make_unique<QScreenCapture>();
m->screen->setScreen(selected);
m->session->setScreenCapture(m->screen.get());
@ -426,6 +431,10 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
}, Qt::QueuedConnection);
connect(m->screen.get(), &QScreenCapture::errorOccurred, this, [failed](QScreenCapture::Error, const QString& error) { failed(error); }, Qt::QueuedConnection);
m->screen->start();
#ifdef UTUN_STREAM_WIN_CURSOR
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop Windows screen capture backend=QtMultimedia cursor_overlay=frame_worker source=%s",
qPrintable(config.source.name));
#endif
}
} else {
auto window = config.source.window.value<QCapturableWindow>();
@ -451,13 +460,13 @@ void StreamPublishEngine::closeSource() {
std::lock_guard<std::mutex> guard(m->lock);
++m->sourceGeneration; m->imagePending = false; m->imageInput = {};
m->frame = {}; m->latestImage = {}; m->previewFrame = {}; m->presentedPreview = {}; m->framePts = -1;
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
m->cursor.reset();
#endif
}
m->screenGrabTimer.reset();
#ifdef UTUN_STREAM_PORTAL
m->portal.reset();
#endif
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
m->cursor.reset();
#endif
emit previewVideoChanged(QVariant());
if (m->sink) disconnect(m->sink.get(), nullptr, this, nullptr);
@ -472,13 +481,14 @@ void StreamPublishEngine::closeSource() {
void StreamPublishEngine::imageLoop() {
#ifdef HAVE_STREAM_CAPTURE
for (;;) {
QVideoFrame frame; uint64_t generation; int64_t pts;
QVideoFrame frame; uint64_t generation; int64_t pts; bool drawCursor;
m->imageProgress("wait-frame");
{
std::unique_lock<std::mutex> lock(m->lock);
m->imageWake.wait(lock, [this] { return m->imageStop || m->imagePending; });
if (m->imageStop) return;
frame = std::move(m->imageInput); generation = m->imageGeneration; pts = m->imagePts; m->imagePending = false;
frame = std::move(m->imageInput); generation = m->imageGeneration; pts = m->imagePts;
drawCursor = m->imageDrawCursor; m->imagePending = false;
}
int64_t convertAt = monotonicUs();
m->imageProgress("convert-frame");
@ -493,6 +503,14 @@ void StreamPublishEngine::imageLoop() {
QMetaObject::invokeMethod(this, [this, generation] { sourceFailed(generation, text("Video frame conversion failed")); });
continue;
}
#ifdef UTUN_STREAM_WIN_CURSOR
std::shared_ptr<StreamScreenCursor> cursor;
{ std::lock_guard<std::mutex> guard(m->lock); cursor = m->cursor; }
if (drawCursor && cursor && !cursor->draw(image)) {
QMetaObject::invokeMethod(this, [this, generation] { sourceFailed(generation, text("Could not capture the mouse cursor")); });
continue;
}
#endif
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
if (frame.rotation() != QtVideo::Rotation::None) image = image.transformed(QTransform().rotate(static_cast<int>(frame.rotation())));
if (frame.mirrored()) image = image.transformed(QTransform().scale(-1, 1));
@ -534,6 +552,7 @@ bool StreamPublishEngine::start(uint64_t group, const StreamPublishConfig& confi
m->origin = m->started = monotonicUs(); m->stats = {}; }
m->convertedFrames = m->encodedFrames = m->deliveredFrames = m->repeatedFrames = 0;
m->videoProgress("starting"); m->captureReport = 0;
m->captureReportRaw = m->captureReportConverted = m->captureReportEncoded = m->captureReportDelivered = 0;
m->id = QRandomGenerator::global()->generate64(); if (!m->id) m->id = 1;
m->group = group; m->audio.reset(); m->audioSequence = 0; m->muted = false;
stream_media_format format{(uint16_t)config.video.width, (uint16_t)config.video.height, (uint16_t)config.video.rotation,
@ -983,13 +1002,22 @@ void StreamPublishEngine::tick() {
sourceFailed(m->generation, text("Timed out waiting for the first video frame"));
{ std::lock_guard<std::mutex> guard(m->lock);
if (monotonicUs() - m->captureReport >= 5000000) {
m->captureReport = monotonicUs();
int64_t reportAt = monotonicUs();
double elapsed = m->captureReport ? (reportAt - m->captureReport) / 1000000.0 : 0.0;
double rawFps = elapsed > 0 ? (m->capturedFrames - m->captureReportRaw) / elapsed : 0.0;
double convertedFps = elapsed > 0 ? (m->convertedFrames.load() - m->captureReportConverted) / elapsed : 0.0;
double encodedFps = elapsed > 0 ? (m->encodedFrames.load() - m->captureReportEncoded) / elapsed : 0.0;
double deliveredFps = elapsed > 0 ? (m->deliveredFrames.load() - m->captureReportDelivered) / elapsed : 0.0;
m->captureReport = reportAt; m->captureReportRaw = m->capturedFrames;
m->captureReportConverted = m->convertedFrames.load(); m->captureReportEncoded = m->encodedFrames.load();
m->captureReportDelivered = m->deliveredFrames.load();
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop capture progress source_generation=%llu video_generation=%llu confirmed=%llu raw_frames=%llu raw_PTS=%lld "
"converted=%llu encoded=%llu delivered=%llu repeated=%llu stage=%s stage_age_ms=%lld",
"raw_fps=%.1f converted=%llu converted_fps=%.1f encoded=%llu encoded_fps=%.1f delivered=%llu delivered_fps=%.1f "
"repeated=%llu stage=%s stage_age_ms=%lld",
(unsigned long long)m->sourceGeneration, (unsigned long long)m->generation, (unsigned long long)m->confirmedGeneration,
(unsigned long long)m->capturedFrames, (long long)m->framePts,
(unsigned long long)m->convertedFrames.load(), (unsigned long long)m->encodedFrames.load(),
(unsigned long long)m->deliveredFrames.load(), (unsigned long long)m->repeatedFrames.load(),
(unsigned long long)m->capturedFrames, (long long)m->framePts, rawFps,
(unsigned long long)m->convertedFrames.load(), convertedFps, (unsigned long long)m->encodedFrames.load(), encodedFps,
(unsigned long long)m->deliveredFrames.load(), deliveredFps, (unsigned long long)m->repeatedFrames.load(),
m->videoStage.load(), (long long)((monotonicUs() - m->videoStageAt.load()) / 1000));
} }
}

Loading…
Cancel
Save