Poll stream image thread cleanup from GUI loop
This commit is contained in:
1 parent
1e21912dc6
commit
49d55a25a1
2 files changed
+23
-13
No files matched your search
@@ -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();
|
||||||
|
|||||||
Reference in new issue
Block a user