From 50fad4ceffd5d8f6536d179cb7fea092a1dcee35 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 02:55:36 +0200 Subject: [PATCH] Preserve video color range and handle capture worker failures --- tools/chatgui/src/stream_publish_engine.cpp | 20 +++++++++++++------- tools/chatgui/src/stream_publish_window.cpp | 1 + tools/chatgui/src/stream_video_encoder.cpp | 7 +++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tools/chatgui/src/stream_publish_engine.cpp b/tools/chatgui/src/stream_publish_engine.cpp index bc6353d3..8bc0a382 100644 --- a/tools/chatgui/src/stream_publish_engine.cpp +++ b/tools/chatgui/src/stream_publish_engine.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include extern "C" { #include "opus_codec.h" @@ -61,10 +63,14 @@ struct StreamPublishEngine::Impl { StreamPublishEngine::StreamPublishEngine(QObject* parent) : QObject(parent), m(std::make_unique()) { m_timer.setInterval(100); connect(&m_timer, &QTimer::timeout, this, &StreamPublishEngine::tick); m_timer.start(); - m->probeThread = std::thread([this] { - auto codecs = supported() ? StreamVideoEncoder::available() : QList(); - std::lock_guard guard(m->lock); m->codecs = std::move(codecs); m->probeReady = true; - }); + try { + m->probeThread = std::thread([this] { + auto codecs = supported() ? StreamVideoEncoder::available() : QList(); + std::lock_guard guard(m->lock); m->codecs = std::move(codecs); m->probeReady = true; + }); + } catch (const std::system_error& error) { + DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop codec probe worker failed: %s", error.what()); m->probeReady = true; + } auto* sound = SoundManager::instance(); connect(sound, &SoundManager::contextAboutToReset, this, [this] { m_capture.close(); }); connect(sound, &SoundManager::contextRestored, this, [this] { if (m->running && m->wanted.audio) openMicrophone(); }); @@ -315,8 +321,8 @@ void StreamPublishEngine::audioLoop() { unsigned take = std::min(960 - used, record.frames - offset); memcpy(pcm + 2 * used, record.pcm + 2 * offset, take * 2 * sizeof(int16_t)); used += take; offset += take; if (used < 960) continue; - stream_publisher_status stats; - { std::lock_guard guard(m->lock); stats = m->stats; } + stream_publisher_status stats{}; + gui_stream_publish_status(m->id, stats); int wanted[] = {stats.compressor_enabled, stats.compressor_max_gain_db ? stats.compressor_max_gain_db : 15, stats.compressor_rise_rate ? stats.compressor_rise_rate : 2}; if (!std::equal(wanted, wanted + 3, settings)) { @@ -378,7 +384,7 @@ void StreamPublishEngine::videoLoop() { if (lastImage.isNull()) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop captured frame conversion failed"); continue; } #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) if (frame.rotation() != QtVideo::Rotation::None) lastImage = lastImage.transformed(QTransform().rotate(static_cast(frame.rotation()))); - if (frame.mirrored()) lastImage = lastImage.mirrored(true, false); + if (frame.mirrored()) lastImage = lastImage.transformed(QTransform().scale(-1, 1)); #else if (frame.rotationAngle()) lastImage = lastImage.transformed(QTransform().rotate(frame.rotationAngle())); #endif diff --git a/tools/chatgui/src/stream_publish_window.cpp b/tools/chatgui/src/stream_publish_window.cpp index c41be073..f625ae0d 100644 --- a/tools/chatgui/src/stream_publish_window.cpp +++ b/tools/chatgui/src/stream_publish_window.cpp @@ -2,6 +2,7 @@ #include "sound_manager.h" #include "debug_config.h" #include +#include #include #include #include diff --git a/tools/chatgui/src/stream_video_encoder.cpp b/tools/chatgui/src/stream_video_encoder.cpp index 503ca885..74b708da 100644 --- a/tools/chatgui/src/stream_video_encoder.cpp +++ b/tools/chatgui/src/stream_video_encoder.cpp @@ -2,6 +2,7 @@ #include "debug_config.h" #include #include +#include extern "C" { #include "../../../src/stream/stream_media.h" #ifdef HAVE_FFMPEG @@ -172,11 +173,13 @@ bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QLi { QPainter painter(&canvas); painter.drawImage(QRect(QPoint((canvas.width() - fitted.width()) / 2, (canvas.height() - fitted.height()) / 2), fitted), image); } int result = av_frame_make_writable(m->frame); - m->scale = sws_getCachedContext(m->scale, canvas.width(), canvas.height(), AV_PIX_FMT_BGRA, + m->scale = sws_getCachedContext(m->scale, canvas.width(), canvas.height(), AV_PIX_FMT_RGB32, m->frame->width, m->frame->height, static_cast(m->frame->format), SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); if (!m->scale) result = AVERROR(ENOMEM); if (result >= 0) { - const uint8_t* input[] = {canvas.constBits()}; int stride[] = {canvas.bytesPerLine()}; + const int* coefficients = sws_getCoefficients(SWS_CS_ITU709); + sws_setColorspaceDetails(m->scale, coefficients, 1, coefficients, 0, 0, 1 << 16, 1 << 16); + const uint8_t* input[] = {canvas.constBits()}; int stride[] = {static_cast(canvas.bytesPerLine())}; result = sws_scale(m->scale, input, stride, 0, canvas.height(), m->frame->data, m->frame->linesize); } m->frame->pts = static_cast(pts); m->frame->pict_type = key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;