Poll stream image thread cleanup from GUI loop

This commit is contained in:
evgeny committed 2026-10-16 23:09:29 +02:00
1 parent 1e21912dc6
commit 49d55a25a1
2 files changed
+23 -13

No files matched your search

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