diff --git a/doc/stream.txt b/doc/stream.txt index 653e3220..15aa0088 100644 --- a/doc/stream.txt +++ b/doc/stream.txt @@ -755,7 +755,7 @@ Android: - Camera/codec callbacks — HandlerThread, Opus encoder — audio capture thread, decoder/AudioTrack — viewer worker. - Native bridge держит pthread mutex, ограниченные очереди и один pending post либо timer. attach/detach выполняются в uasync вне callbacks. detach отменяет post/timer, закрывает handles, - очищает media, snapshots и запрещает producers до уничтожения instance/ua. + очищает media, снимает наблюдение каталога и запрещает producers до уничтожения instance/ua. UI не сохраняет borrowed inst/ua. Idle без pending data не будит мобильное ядро. - TX<=2MiB/128 records, не более 250ms ожидания. Actor публикует <=2 пачек за вызов, неполную пачку объединяет с аудио/видео до 20ms; продолжение работы через timer 5ms. @@ -771,8 +771,16 @@ Android: своего микрофона, чтобы исключить акустическую обратную связь. Камера при закрытии preview продолжает работать. Viewer закрывается на Activity ON_STOP и создаётся заново на ON_START; publisher от UI не зависит. Видимый player удерживает экран включённым. Preview ограничен оставшимся местом, кнопки управления не перекрывает. -- Source list строится в uasync по stream_member_load и stream_member_online, выдаётся UI копией JSON. - Свежий snapshot содержит source ID, epoch, self и экранированное имя. Увиденный источник не создаёт подписку. +- Source list общий для Android/desktop: stream_sources_json в uasync использует stream_member_load + и stream_member_online. stream_watch_sources наблюдает один выбранный канал; group=0 снимает наблюдение. + Начальный snapshot и изменения после Merkle/BGP передаются через CHAT_EVT_STREAM_SOURCES=58: + group_id:8BE, valid:1, JSON с id(decimal string), epoch(hex string), self, name. + События объединяются отложенным callback, одинаковый список повторно не отправляется. Таймера опроса нет. + Обнаружение не создаёт подписку, media-буфер и не удерживает ядро в ACTIVE. +- В шапке канала значок виден при наличии источников; один источник открывается сразу, + несколько выбираются из списка по имени/ID. Пункт меню «Трансляции» также открывает управление публикацией. + Выбранная эпоха проверяется actor до подписки и по фактически открытому handle. Перезапуск источника + требует нового выбора. Ошибка viewer kind=6 передаёт stream_close_reason в flags. - Stop сначала завершает capture producers и encoder EOS (максимум 1s), затем FIN. Publisher сохраняет конечный буфер ещё 2s для сетевой выдачи, затем закрывается. Это ограниченный drain, не подтверждение воспроизведения всеми viewers. Abort/core restart очищает очередь сразу. @@ -785,3 +793,24 @@ Kotlin progress — DEBUG раз в секунду; C packet-level диагно Проверки: test_stream_media (все пары потерь 6+2, parser/границы/GAP), test_android_stream (живое ядро, preview, verified snapshots, FIN/cancel, concurrent producer/detach и leak count), Android H264AnnexBTest (Annex B/length-prefixed NAL, SPS/PPS/IDR и усечённые данные). + +Desktop: +- Значок трансляций расположен в шапке MessageList. Источники берутся из того же каталога ядра; + ответы предыдущего канала не меняют текущую шапку. Личные беседы снимают наблюдение. +- Один StreamPlayerWindow на процесс. Он содержит имя источника, состояние подключения, громкость, + полноэкранный режим и закрытие. Выбор нового источника закрывает предыдущего reader. +- transport/stream_bridge владеет одним viewer и ограничивает RX 4MiB/256 записей. + Attach/detach выполняются в uasync; команды и копирование медиаданных защищены mutex. + Post отменяется до destroy ядра; GUI/decoder не хранят заимствованные instance/handle pointers. +- stream_media_receiver собирает H.264 access units и выдаёт Opus 48kHz mono/20ms. + FFmpeg/Opus декодируются отдельным worker; Qt-очередь не накапливает кадры. + Очереди ограничены шестью декодированными видеокадрами и 25 аудиопакетами (500ms). + Видео декодируется без задержки frame-threading. При GAP сбрасываются decoder refs и timeline до KEY. +- После prebuffer 150ms показ следует физическим audio callback timestamps; без звука — монотонным часам. + Короткие пропуски аудио заполняются Opus PLC; большой разрыв/pressure сбрасывает timeline. + Закрытие окна снимает только reader, собственная публикация сохраняется. + Удаление канала сообщает STREAM_CLOSE_GROUP; остановка ядра снимает media и catalogue owners. +- Без FFmpeg окно сообщает, что декодирование недоступно. Нужна desktop-сборка с HAVE_FFMPEG. +Проверки: test_stream_view — настоящее ядро, старые эпохи/cancel, RX pressure, H.264 с поворотом, +Opus/громкость на настоящем null audio backend, закрытие канала и полный баланс tracked allocations. +test_stream_service также проверяет общий каталог двух независимых источников одного канала.