From 736fb92ed739c4417147f0f0b0d0a36d26264cbe Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 13 Oct 2026 12:57:37 +0200 Subject: [PATCH] Document live playback queue bounds and latency regression --- doc/stream.txt | 8 ++++++-- doc/tasks.md | 8 ++++++++ tools/chatgui/src/streamplayer_window.cpp | 7 ++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/doc/stream.txt b/doc/stream.txt index 543ec6f2..b5ab4a9e 100644 --- a/doc/stream.txt +++ b/doc/stream.txt @@ -857,7 +857,9 @@ Desktop: Post отменяется до destroy ядра; GUI/decoder не хранят заимствованные instance/handle pointers. - stream_media_receiver собирает H.264 access units и выдаёт Opus 48kHz mono/20ms. FFmpeg/Opus декодируются отдельным worker; Qt-очередь не накапливает кадры. - Очереди ограничены шестью декодированными видеокадрами и 25 аудиопакетами (500ms). + Очередь декодированного видео ограничена 128 кадрами и 256MiB, аудио — 25 пакетами (500ms). + Кадры сохраняются до достижения их PTS аудиочасами. При пределе видео отбрасывается новый + уже декодированный кадр; ближайшие к показу остаются в очереди, codec refs не меняются. Видео декодируется без задержки frame-threading. При GAP сбрасываются decoder refs и timeline до KEY. - После prebuffer 150ms показ следует физическим audio callback timestamps; без звука — монотонным часам. Короткие пропуски аудио заполняются Opus PLC; большой разрыв/pressure сбрасывает timeline. @@ -865,7 +867,9 @@ Desktop: Удаление канала сообщает STREAM_CLOSE_GROUP; остановка ядра снимает media и catalogue owners. - Без FFmpeg окно сообщает, что декодирование недоступно. Нужна desktop-сборка с HAVE_FFMPEG. Проверки: test_stream_view — настоящее ядро, старые эпохи/cancel, RX pressure, H.264 с поворотом и сменой revision без новой эпохи, -Opus/громкость на настоящем null audio backend, закрытие канала и полный баланс tracked allocations. +Opus/громкость на настоящем null audio backend, непрерывные 30fps при физической audio latency 200ms, +закрытие канала и полный баланс tracked allocations. Раз в секунду desktop viewer логирует decode/show FPS, +late/pressure drops, число и байты кадров, PTS границ очереди относительно часов и audio callback timing. test_stream_service также проверяет общий каталог двух независимых источников одного канала. 21. Суперузлы: помощь в раздаче по запросу diff --git a/doc/tasks.md b/doc/tasks.md index f3b183af..52c8017c 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -4,6 +4,14 @@ ## Текущие задачи +[+] **Desktop live: очередь видео вытесняла будущие кадры до показа** — 13.10.2026: + SM → colo2 доставлял ~30fps, desktop декодировал ~30fps, но videoQ=6 постоянно + выбрасывал ближайший кадр до достижения его PTS аудиочасами. Регрессия H.264/Opus + 30fps с физической audio latency 200ms на старом коде не показала ни одного кадра. + Видео ограничено 128 кадрами/256MiB; при пределе сохраняются ближайшие к показу. + Добавлены decode/show FPS, late/pressure и PTS очереди относительно часов; + поштучное нормальное вытеснение транспортного буфера перенесено в trace. + [+] **Android: категории диагностики и обрезанный текст уровня** — 13.10.2026: список берётся из native-ядра, включая stream/video/reality/dm/call/aec. Поля уровня используют минимальную высоту вместо фиксированных 48 dp; diff --git a/tools/chatgui/src/streamplayer_window.cpp b/tools/chatgui/src/streamplayer_window.cpp index 90abb807..649e36fc 100644 --- a/tools/chatgui/src/streamplayer_window.cpp +++ b/tools/chatgui/src/streamplayer_window.cpp @@ -181,7 +181,7 @@ void StreamPlayerWindow::decode() { opus_codec_decoder_t* opus = nullptr; bool waitKey = true, opened = false; int64_t deadline = nowUs() + 10000000, reportAt = nowUs(), expectedAudio = -1; - uint64_t decoded = 0, discarded = 0, late = 0, pressureDropped = 0, previousDecoded = 0, previousShown = 0; + uint64_t decoded = 0, discarded = 0, late = 0, pressureDropped = 0, previousPressure = 0, previousDecoded = 0, previousShown = 0; uint32_t videoRevision = 0; auto reset = [&] { avcodec_free_context(&decoder); @@ -317,9 +317,10 @@ void StreamPlayerWindow::decode() { (long long)(p.videos.empty() ? 0 : p.videos.front().pts - clock), (long long)(p.videos.empty() ? 0 : p.videos.back().pts - clock), p.audios.size(), (long long)(p.audioPts < 0 ? 0 : now - p.audioAt), (unsigned long long)p.underruns); - if (pressureDropped) DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop video queue pressure id=%llu dropped=%llu; preserve due frames", + if (pressureDropped != previousPressure) + DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop video queue pressure id=%llu dropped=%llu; preserve due frames", (unsigned long long)p.id, (unsigned long long)pressureDropped); - previousDecoded = decoded; previousShown = p.shown; + previousDecoded = decoded; previousShown = p.shown; previousPressure = pressureDropped; } } avcodec_free_context(&decoder); av_frame_free(&frame); av_packet_free(&packet);