diff --git a/tools/chatgui/src/stream_publish_engine.cpp b/tools/chatgui/src/stream_publish_engine.cpp index 8476db5e..553770ba 100644 --- a/tools/chatgui/src/stream_publish_engine.cpp +++ b/tools/chatgui/src/stream_publish_engine.cpp @@ -64,7 +64,7 @@ struct StreamPublishEngine::Impl { std::mutex lock; std::condition_variable wake; std::thread videoThread, audioThread, joinThread; - std::thread imageThread, imageJoinThread; + std::unique_ptr imageThread; std::atomic workersJoined{false}, videoExited{false}, audioExited{false}, imageJoined{false}; bool stopping = false, finSent = false, destroying = false; int64_t stopStarted = 0, stopReport = 0; @@ -156,13 +156,14 @@ StreamPublishEngine::StreamPublishEngine(QObject* parent) : QObject(parent), m(s m_capture.close(); // Контекст нельзя освободить до завершения фонового закрытия устройства. }); #ifdef HAVE_STREAM_CAPTURE - m->imageThread = std::thread([this] { + m->imageThread.reset(QThread::create([this] { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "desktop image worker started tid=%llu", (unsigned long long)(quintptr)QThread::currentThreadId()); imageLoop(); m->imageProgress("release-thread-resources"); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "desktop image loop returned; releasing thread resources frames=%llu tid=%llu", (unsigned long long)m->imageFrames.load(), (unsigned long long)(quintptr)QThread::currentThreadId()); - }); + })); + m->imageThread->start(); #else m->imageJoined = true; #endif @@ -177,15 +178,8 @@ StreamPublishEngine::~StreamPublishEngine() { #ifdef HAVE_STREAM_CAPTURE { std::lock_guard guard(m->lock); m->imageStop = true; m->imagePending = false; m->imageInput = {}; } m->imageWake.notify_all(); - m->imageJoinThread = std::thread([this] { - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "desktop image join begin stage=%s stage_age_ms=%lld", - m->imageStage.load(), (long long)((monotonicUs() - m->imageStageAt.load()) / 1000)); - m->imageThread.join(); - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "desktop image join complete; native thread resources released"); - m->imageJoined = true; - }); #endif - if (!finishStop() || !m->imageJoined) { + if (!finishStop() || !finishImageStop()) { // Qt освобождает thread-local графику через GUI. Даже при выходе приложения нельзя блокировать его join(). DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop publisher destruction waits for workers stopping=%d image_joined=%d image_stage=%s", m->stopping, m->imageJoined.load(), m->imageStage.load()); @@ -196,7 +190,8 @@ StreamPublishEngine::~StreamPublishEngine() { if (firstPoll) { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "desktop publisher destruction GUI timer active"); firstPoll = false; } - if (finishStop() && m->imageJoined) { loop.quit(); return; } + const bool imageDone = finishImageStop(); + if (finishStop() && imageDone) { loop.quit(); return; } const int64_t now = monotonicUs(); if (now - reportAt < 5000000) return; reportAt = now; @@ -213,7 +208,6 @@ StreamPublishEngine::~StreamPublishEngine() { if (m->stopping || !m->imageJoined) DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "desktop publisher destruction GUI loop exited before worker cleanup completed"); } - if (m->imageJoinThread.joinable()) m->imageJoinThread.join(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "desktop publisher destruction complete elapsed_ms=%lld", (long long)((monotonicUs() - destroyAt) / 1000)); } bool StreamPublishEngine::supported() { @@ -664,6 +658,21 @@ bool StreamPublishEngine::finishStop() { return true; } +/* Проверка QThread с нулевым таймаутом сохраняет GUI event loop доступным для native cleanup. */ +bool StreamPublishEngine::finishImageStop() { + if (m->imageJoined) return true; + if (!m->imageThread) { + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "desktop image worker missing before native thread completion"); + return false; + } + if (!m->imageThread->wait(0)) return false; + m->imageThread.reset(); + m->imageJoined = true; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "desktop image native thread cleanup completed frames=%llu", + (unsigned long long)m->imageFrames.load()); + return true; +} + uint64_t StreamPublishEngine::softwareEncodingRequest() const { std::lock_guard guard(m->lock); return m->softwareRequest; } diff --git a/tools/chatgui/src/stream_publish_engine.h b/tools/chatgui/src/stream_publish_engine.h index 6c594e25..dd026f23 100644 --- a/tools/chatgui/src/stream_publish_engine.h +++ b/tools/chatgui/src/stream_publish_engine.h @@ -82,6 +82,7 @@ private: void publicationFailed(uint64_t id, const QString& error); void tick(); bool finishStop(); + bool finishImageStop(); void imageLoop(); void videoLoop(StreamPublishConfig initial); void audioLoop();