Drain publisher while asynchronous microphone teardown completes

This commit is contained in:
evgeny committed 2026-10-16 17:34:21 +02:00
1 parent 114c0b3e2f
commit 16e547d15d
2 files changed
+17 -8

No files matched your search

+10 -3
View File
@@ -120,6 +120,9 @@ Android worker держит один рабочий input и один готов
input buffer получает лишь для уже готовых данных и сразу возвращает codec.
Первый decoded frame разрешает движение clock/audio. Будущий output ждёт PTS и
приостанавливает новые video requests, сохраняя независимую обработку audio/control.
Если первый доступный звук позже KEY, cursor отсчитывает стартовый audio lookahead от
первого аудиопакета, сохраняя это основание неизменным. Opus/SoundTouch получает PCM до
запуска audio clock; неподвижные часы не разрешают читать дальше этого ограниченного окна.
PTS поданных кадров служат диагностике/проверке запаздывания, а не лимиту заполнения codec.
Смена revision заменяет только video decoder и его измеренный защитный guard;
GAP/EXPIRED начинают новую точку KEY. Нехватка сети сохраняет decoder references.
@@ -145,10 +148,14 @@ Qt capture callback хранит только последний кадр, video
вне GUI и hardware callbacks. Отдельные поколения источника и encoder позволяют менять
видеокодек/bitrate/rotation без повторного выбора экрана в портале. Ошибки Qt capture
обрабатываются через queued callback, чтобы не уничтожать backend из собственного callback.
Сначала stop запрещает producers, закрывает Qt capture и подписку на аудиозахват, затем join workers,
освобождает microphone lease и завершает публикацию через FIN с drain 2s.
Stop запрещает producers, закрывает Qt capture и подписку на аудиозахват. Отдельный join worker
дожидается полного выхода video/audio threads, включая native TLS Qt; GUI продолжает обрабатывать события.
После join отправляется FIN с drain 2s; закрытие последнего микрофона может продолжаться параллельно.
Microphone lease и owner сохраняются до завершения обоих путей. Новый start ждёт также FIN drain.
При завершении приложения engine уничтожается до SoundManager и ядра; detach в uasync
закрывает оставшийся publisher. Workers никогда не держат borrowed instance/handle.
закрывает оставшийся publisher. Destructor ждёт workers в локальном event loop без пользовательского ввода,
чтобы thread cleanup мог обратиться к GUI; start и внешние сигналы engine в это время запрещены.
Workers никогда не держат borrowed instance/handle.
Microphone lease публикации исключает call/recorder; рация согласует вход через `AudioCapture`.
Одинаковый выбранный вход имеет одну аппаратную сессию и профиль трансляции. Разные входы независимы.
Подключение/отключение рации не останавливает устройство трансляции: атомарный слот и счётчик readers
+7 -5
View File
@@ -63,7 +63,7 @@ struct StreamPublishEngine::Impl {
std::condition_variable wake;
std::thread videoThread, audioThread, joinThread;
std::atomic<bool> workersJoined{false}, videoExited{false}, audioExited{false};
bool stopping = false, destroying = false;
bool stopping = false, finSent = false, destroying = false;
int64_t stopStarted = 0, stopReport = 0;
std::atomic<bool> running{false}, muted{false};
std::atomic<unsigned> audioDropped{0}, audioMissing{0};
@@ -494,7 +494,7 @@ void StreamPublishEngine::publicationFailed(uint64_t id, const QString& error) {
void StreamPublishEngine::stop() {
if (!m->running.exchange(false)) return;
int64_t started = monotonicUs();
m->stopping = true; m->stopStarted = m->stopReport = started;
m->stopping = true; m->finSent = false; m->stopStarted = m->stopReport = started;
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop stop begin id=%llu video_stage=%s stage_age_ms=%lld converted=%llu encoded=%llu delivered=%llu",
(unsigned long long)m->id, m->videoStage.load(), (long long)((started - m->videoStageAt.load()) / 1000),
(unsigned long long)m->convertedFrames.load(), (unsigned long long)m->encodedFrames.load(),
@@ -522,11 +522,13 @@ bool StreamPublishEngine::finishStop() {
if (!m->workersJoined) return false;
m->joinThread.join();
}
if (!m->finSent) {
gui_stream_publish_stop(m->id); m->finSent = true;
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop workers stopped id=%llu elapsed_ms=%lld microphone_closing=%d; draining publisher",
(unsigned long long)m->id, (long long)((monotonicUs() - m->stopStarted) / 1000), m_capture.closing());
}
if (m_capture.closing()) return false;
m_microphone.release(); m->stopping = false;
gui_stream_publish_stop(m->id);
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop capture stopped id=%llu elapsed_ms=%lld; draining publisher",
(unsigned long long)m->id, (long long)((monotonicUs() - m->stopStarted) / 1000));
return true;
}