From 366cf94e15bd1559675670fb094b322d2b258302 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 00:07:52 +0200 Subject: [PATCH] Describe shared ring playback, corrected reserve history and stereo catchup --- doc/stream.txt | 68 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/doc/stream.txt b/doc/stream.txt index 72d5e681..826038b1 100644 --- a/doc/stream.txt +++ b/doc/stream.txt @@ -800,13 +800,15 @@ Android: неполную пачку объединяет с аудио/видео до 20ms; продолжение работы через timer 5ms. При video pressure отбрасываются целые ожидающие кадры, P-кадры запрещены до нового KEY. Кодировщик получает отказ admission и запрашивает KEY с ограничением частоты 500ms. -- RX<=4MiB/256 records. Pressure очищает media, сохраняет control results и сообщает GAP. - Kotlin viewer ограничивает video queue 250ms/128 кадрами/2MiB, audio queue 16 пакетами. - За проход подаётся до 8 видео-кадров, выход декодера обслуживается до 16 раз. - Нельзя выбросить reference P-кадр и продолжить с зависимыми: переполнение сбрасывает декодер до KEY. +- RX<=4MiB/256 records, подготовлено не более двух media records. Запас остаётся в общем RS-кольце; + bridge читает ближайшие кадры по запросу worker с текущим PTS и отдельными audio/video lookahead. + Kotlin держит до четырёх входных кадров, до трёх поданных decoder PTS и один ожидающий выход. + За проход подаётся до четырёх кадров, выход декодера обслуживается до восьми раз. + При обычной нехватке данных decoder references сохраняются; GAP/вытеснение требуют нового KEY. Просроченный кадр при нормальной последовательности декодируется без показа. -- После prebuffer 150ms AudioTrack clock определяет время показа видео; видео без аудио использует - монотонные часы. Короткие audio gaps заполняются Opus PLC, большой разрыв сбрасывает timeline. +- Фиксированного prebuffer нет. AudioTrack задаёт время показа видео по реально услышанному PCM; + без аудио используются монотонные часы с остановкой на границе готовых кадров. + Короткие audio gaps заполняются Opus PLC, большой разрыв сбрасывает timeline. Локальный preview использует обычную подписку на собственный источник с отключённым воспроизведением своего микрофона, чтобы исключить акустическую обратную связь. Локальный preview ориентируется по сенсору своей камеры и текущему положению экрана; верх картинки зрителей отмечается красной гранью на кнопках поворота. @@ -839,7 +841,8 @@ Android: Каждая запись JNI pull имеет header 24 bytes: kind:1, flags:1, width:2, height:2, rotation:2, PTS_us:8, length:4, revision:4. Kotlin и desktop viewer пересоздают только видеодекодер при смене revision; audio clock не сбрасывается. - Stop сначала закрывает capture producers и encoder, затем FIN. - Publisher сохраняет конечный буфер ещё 2s для сетевой выдачи, затем закрывается. + Publisher сохраняет конечный буфер ещё 2s для сетевой выдачи, затем снимает своё владение. + При наличии локального reader завершённый источник и кольцо живут до его close, позволяя дочитать FIN. Это ограниченный drain, не подтверждение воспроизведения всеми viewers. Abort/core restart очищает очередь сразу. Поздние команды старого session ID не меняют новый запуск. - Звонок, рация и voice recording не начинают новый capture во время трансляции; @@ -870,29 +873,60 @@ Desktop: ответы предыдущего канала не меняют текущую шапку. Личные беседы снимают наблюдение. - Один StreamPlayerWindow на процесс. Он содержит имя источника, состояние подключения, громкость, полноэкранный режим и закрытие. Выбор нового источника закрывает предыдущего reader. -- transport/stream_bridge владеет одним viewer и ограничивает RX 4MiB/256 записей. +- transport/stream_bridge владеет одним viewer, готовит до двух media records и ограничивает RX 4MiB/16 записей. Attach/detach выполняются в uasync; команды и копирование медиаданных защищены mutex. Post отменяется до destroy ядра; GUI/decoder не хранят заимствованные instance/handle pointers. -- stream_media_receiver собирает H.264 access units и выдаёт Opus 48kHz mono/stereo/20ms. +- stream_media_cursor собирает ближайший H.264 access unit и выдаёт Opus 48kHz mono/stereo/20ms из RS-кольца. Android AudioTrack и desktop output работают в stereo; mono-пакеты upmix выполняет libopus. FFmpeg/Opus декодируются отдельным worker; Qt-очередь не накапливает кадры. - Очередь декодированного видео ограничена 128 кадрами и 256MiB, аудио — 25 пакетами (500ms). - Кадры сохраняются до достижения их PTS аудиочасами. При пределе видео отбрасывается новый - уже декодированный кадр; ближайшие к показу остаются в очереди, codec refs не меняются. + Worker запрашивает видео при наличии менее двух готовых изображений; защитный предел — четыре. + Дальние кадры и звук не копируются из кольца. SoundTouch и устройство держат только рабочую порцию PCM. + Кадры сохраняются до достижения их PTS аудиочасами; при задержке GUI общий clock останавливается. Видео декодируется без задержки frame-threading. При GAP сбрасываются decoder refs и timeline до KEY. -- При начальном подключении desktop/Android прогревают Opus до первого KEY в ограниченной очереди. +- При начальном подключении desktop/Android прогревают Opus из кольца до первого KEY (до 500ms истории). Старый звук обрезается по PTS видео, включая начало stereo PCM-пакета; декодер не сбрасывается из-за preroll. - Prebuffer 150ms начинается после выбора первого KEY, затем показ следует физическим audio callback timestamps; - без звука — монотонным часам. END до KEY освобождает preroll, не оставляя незавершённую очередь. + Фиксированного prebuffer нет: показ следует физическим audio callback timestamps; + без звука — монотонным часам с ограничением готовым PTS. END до KEY завершает пустой reader. Короткие пропуски аудио заполняются Opus PLC; большой разрыв/pressure сбрасывает timeline. Закрытие окна снимает только reader, собственная публикация сохраняется. Удаление канала сообщает STREAM_CLOSE_GROUP; остановка ядра снимает media и catalogue owners. - Без FFmpeg окно сообщает, что декодирование недоступно. Нужна desktop-сборка с HAVE_FFMPEG. -Проверки: test_stream_view — настоящее ядро, старые эпохи/cancel, RX pressure, H.264 с поворотом и сменой revision без новой эпохи, +Проверки: test_stream_view — настоящее ядро, старые эпохи/cancel, задержанное чтение без роста RX, H.264 с поворотом и сменой revision без новой эпохи, 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 также проверяет общий каталог двух независимых источников одного канала. +test_stream_service также проверяет общий каталог двух независимых источников одного канала, +шестисекундную паузу readers при живом сетевом приёме без restart и полное локальное/удалённое дочитывание FIN. + +Общий запас и адаптация воспроизведения: +- Кодированный поток хранится один раз, в stream_buffer (RS-кольцо 32MiB по умолчанию). + stream_media_store хранит только диапазоны seq, PTS готового видео/аудио и позиции KEY. + Индекс ограничен геометрией кольца и 4MiB метаданных; cursor принадлежит отдельному handle. + Live ingest cursor и оптимизатор выдач продолжают продвигаться, независимо от паузы player. + Рабочая память reader — один H.264 кадр до 2MiB, один Opus-пакет и два scratch медиаблока. +- Подпись пачки проверяется при первом восстановлении. Индексы использованных RS-частей фиксируются; + повторное чтение использует тот же неизменяемый набор, поэтому поздние части не меняют проверенные данные. + Reader не закрепляет старые части против вытеснения. После выхода за окно кольца он начинает с нового KEY. +- AVAILABLE сообщает готовность индекса, DATA/GAP/END callbacks описывают live ingest. + media_read выдаёт END после обоих треков конкретного reader. MORE продолжает ограниченную сборку + отдельным callback без ожидания новых пакетов; WAIT ожидает новые данные или изменение позиции. +- Общий C-контроллер хранит 60 секундных минимумов запаса: ready_PTS - playback_PTS. + ready_PTS — меньшая из границ полного видео и звука, для preview без звука — граница видео. + Каждое обновление учитывается в минимуме текущей секунды, включая короткий простой. + История нормализована через correction += wall_elapsed - actually_played_media_elapsed. + Старые минимумы автоматически увеличиваются на длительность паузы и уменьшаются на реальный расход при догоне. + Измерения опираются на PTS фактически услышанного PCM, включая аппаратную очередь и периоды тишины. +- После полной здоровой минуты темп плавно повышается до x1.1; SoundTouch сохраняет тон и стерео. + Ускорение расходует только минимальный запас за минуту сверх безопасной границы (не менее 33ms, + с учётом аппаратной задержки), уже поставленного ускоренного звука и плавного торможения. + При нехватке данных темп возвращается к x1.0, часы ждут, сетевой приём продолжает накапливать запас. + После возобновления отсчёт здоровой минуты начинается заново. Разрыв/GAP/новая эпоха сбрасывают историю. +- Диагностика stream: PTS, ready, текущий/минимальный запас, correction, healthy interval, tempo, + pending gain, wait/resume, generation, причины resync и размеры рабочих очередей. DEBUG — раз в секунду. + test_stream_playback проверяет историю, плавный догон с отложенной аппаратной очередью, + независимые readers, позднюю повреждённую RS-часть, вытеснение, GAP, FIN и большой многопачечный кадр. + test_stream_audio проверяет настоящий стерео Opus/SoundTouch x1.1, сохранение тона/баланса, + отображение аппаратного frame counter в исходный PTS и остановку PTS на тишине. 21. Суперузлы: помощь в раздаче по запросу