Keep stream capture failures and cursor snapshots tied to their source
This commit is contained in:
1 parent
9c7d088f8b
commit
fd73fae77f
3 files changed
+41
-25
No files matched your search
@@ -502,6 +502,9 @@ void StreamPublishEngine::imageLoop() {
|
||||
#ifdef HAVE_STREAM_CAPTURE
|
||||
for (;;) {
|
||||
QVideoFrame frame; uint64_t generation; int64_t pts; bool drawCursor;
|
||||
#ifdef UTUN_STREAM_WIN_CURSOR
|
||||
std::shared_ptr<StreamScreenCursor> cursor;
|
||||
#endif
|
||||
m->imageProgress("wait-frame");
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m->lock);
|
||||
@@ -509,6 +512,10 @@ void StreamPublishEngine::imageLoop() {
|
||||
if (m->imageStop) return;
|
||||
frame = std::move(m->imageInput); generation = m->imageGeneration; pts = m->imagePts;
|
||||
drawCursor = m->imageDrawCursor; m->imagePending = false;
|
||||
if (!m->running || generation != m->sourceGeneration) continue;
|
||||
#ifdef UTUN_STREAM_WIN_CURSOR
|
||||
cursor = m->cursor;
|
||||
#endif
|
||||
}
|
||||
int64_t convertAt = monotonicUs();
|
||||
m->imageProgress("convert-frame");
|
||||
@@ -520,25 +527,30 @@ void StreamPublishEngine::imageLoop() {
|
||||
if (image.isNull()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop captured frame conversion failed generation=%llu format=%d size=%dx%d",
|
||||
(unsigned long long)generation, frame.pixelFormat(), frame.width(), frame.height());
|
||||
QMetaObject::invokeMethod(this, [this, generation] { sourceFailed(generation, text("Video frame conversion failed")); });
|
||||
QMetaObject::invokeMethod(this, [this, generation] {
|
||||
if (generation == m->sourceGeneration) sourceFailed(m->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")); });
|
||||
if (drawCursor && (!cursor || !cursor->draw(image))) {
|
||||
if (!cursor) DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor missing for captured frame generation=%llu", (unsigned long long)generation);
|
||||
QMetaObject::invokeMethod(this, [this, generation] {
|
||||
if (generation == m->sourceGeneration) sourceFailed(m->generation, text("Could not capture the mouse cursor"));
|
||||
});
|
||||
continue;
|
||||
}
|
||||
QVideoFrame preview = previewFrameFromImage(image);
|
||||
if (!preview.isValid()) DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop preview frame unavailable generation=%llu",
|
||||
(unsigned long long)generation);
|
||||
#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));
|
||||
#else
|
||||
if (frame.rotationAngle()) image = image.transformed(QTransform().rotate(frame.rotationAngle()));
|
||||
#endif
|
||||
#ifdef UTUN_STREAM_WIN_CURSOR
|
||||
QVideoFrame preview = previewFrameFromImage(image);
|
||||
if (!preview.isValid()) DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop preview frame unavailable generation=%llu",
|
||||
(unsigned long long)generation);
|
||||
#endif
|
||||
m->imageProgress("publish-image");
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* Системный курсор Windows/X11 накладывается на QScreen::grabWindow до масштабирования.
|
||||
* Только GUI-поток. Положение, hotspot и bitmap используются в физических пикселях выбранного монитора. */
|
||||
/* Системный курсор накладывается до масштабирования. Создаётся в GUI: X11 draw остаётся в GUI,
|
||||
* Windows draw работает в image worker через сохранённый HMONITOR, без обращения к QScreen.
|
||||
* Положение, hotspot и bitmap используют физические пиксели выбранного монитора. */
|
||||
#pragma once
|
||||
#include <QImage>
|
||||
#include <QPointer>
|
||||
@@ -14,8 +15,11 @@ public:
|
||||
~StreamScreenCursor();
|
||||
bool draw(QImage& frame);
|
||||
private:
|
||||
#ifdef Q_OS_WIN
|
||||
void* m_monitor = nullptr;
|
||||
QString m_screenName;
|
||||
#else
|
||||
QPointer<QScreen> m_screen;
|
||||
#ifndef Q_OS_WIN
|
||||
QWindow m_origin;
|
||||
struct _XDisplay* m_display = nullptr;
|
||||
#endif
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
#include <qt_windows.h>
|
||||
#include <cstring>
|
||||
|
||||
StreamScreenCursor::StreamScreenCursor(QScreen* screen) : m_screen(screen) {
|
||||
StreamScreenCursor::StreamScreenCursor(QScreen* screen) {
|
||||
if (!screen) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: Windows screen unavailable"); return; }
|
||||
m_screenName = screen->name();
|
||||
auto* native = screen->nativeInterface<QNativeInterface::QWindowsScreen>();
|
||||
if (!native || !(m_monitor = native->handle())) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: native monitor unavailable screen=%s", qPrintable(m_screenName)); return;
|
||||
}
|
||||
m_ready = true;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop cursor opened screen=%s scale=%.3f backend=Win32",
|
||||
qPrintable(screen->name()), screen->devicePixelRatio());
|
||||
qPrintable(m_screenName), screen->devicePixelRatio());
|
||||
}
|
||||
|
||||
StreamScreenCursor::~StreamScreenCursor() {
|
||||
@@ -19,30 +24,25 @@ StreamScreenCursor::~StreamScreenCursor() {
|
||||
|
||||
/* DrawIconEx работает с фоном кадра: это сохраняет и alpha, и AND/XOR системных курсоров. */
|
||||
bool StreamScreenCursor::draw(QImage& frame) {
|
||||
if (!m_ready || !m_screen || frame.isNull()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor unavailable ready=%d screen=%d frame=%dx%d",
|
||||
m_ready, !m_screen.isNull(), frame.width(), frame.height()); return false;
|
||||
if (!m_ready || !m_monitor || frame.isNull()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor unavailable ready=%d monitor=%p frame=%dx%d",
|
||||
m_ready, m_monitor, frame.width(), frame.height()); return false;
|
||||
}
|
||||
CURSORINFO cursor{}; cursor.cbSize = sizeof(cursor);
|
||||
if (!GetCursorInfo(&cursor)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: GetCursorInfo failed error=%lu", GetLastError()); return false;
|
||||
}
|
||||
if (!(cursor.flags & CURSOR_SHOWING)) return true;
|
||||
auto* native = m_screen->nativeInterface<QNativeInterface::QWindowsScreen>();
|
||||
if (!native) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: native monitor unavailable screen=%s", qPrintable(m_screen->name()));
|
||||
return false;
|
||||
}
|
||||
MONITORINFOEXW monitor{}; monitor.cbSize = sizeof(monitor);
|
||||
if (!GetMonitorInfoW(native->handle(), reinterpret_cast<MONITORINFO*>(&monitor))) {
|
||||
if (!GetMonitorInfoW(static_cast<HMONITOR>(m_monitor), reinterpret_cast<MONITORINFO*>(&monitor))) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: GetMonitorInfo failed screen=%s error=%lu",
|
||||
qPrintable(m_screen->name()), GetLastError()); return false;
|
||||
qPrintable(m_screenName), GetLastError()); return false;
|
||||
}
|
||||
const QSize monitorSize(monitor.rcMonitor.right - monitor.rcMonitor.left,
|
||||
monitor.rcMonitor.bottom - monitor.rcMonitor.top);
|
||||
if (frame.size() != monitorSize) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: captured frame does not match monitor screen=%s frame=%dx%d monitor=%dx%d",
|
||||
qPrintable(m_screen->name()), frame.width(), frame.height(), monitorSize.width(), monitorSize.height());
|
||||
qPrintable(m_screenName), frame.width(), frame.height(), monitorSize.width(), monitorSize.height());
|
||||
return false;
|
||||
}
|
||||
// RECT использует физические координаты общего виртуального desktop, включая отрицательные origins.
|
||||
@@ -105,7 +105,7 @@ bool StreamScreenCursor::draw(QImage& frame) {
|
||||
painter.drawImage(area.topLeft(), composed);
|
||||
if (!m_reported) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop cursor drawn screen=%s device=%ls origin=%d,%d position=%d,%d hotspot=%lu,%lu sprite=%dx%d frame=%dx%d",
|
||||
qPrintable(m_screen->name()), monitor.szDevice, origin.x(), origin.y(), position.x(), position.y(), icon.xHotspot,
|
||||
qPrintable(m_screenName), monitor.szDevice, origin.x(), origin.y(), position.x(), position.y(), icon.xHotspot,
|
||||
icon.yHotspot, size.width(), size.height(), frame.width(), frame.height());
|
||||
m_reported = true;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user