Document multiview ownership, independent clocks and validation
This commit is contained in:
1 parent
b3ebc09b9b
commit
03f7d4341d
2 files changed
+63
-18
No files matched your search
+50
-18
@@ -794,7 +794,7 @@ Desktop и Android открывают просмотр через stream_media_o
|
||||
Ретрансляторы не разбирают uAV2 и не пересоздают подписи источника.
|
||||
|
||||
Android:
|
||||
- Один foreground publisher на процесс, один viewer. Каждая операция имеет новый session ID.
|
||||
- Один foreground publisher на процесс; каждый viewer имеет независимый session ID.
|
||||
- StreamService владеет камерой, подпиской на микрофон и PARTIAL_WAKE_LOCK; пересоздание Activity не завершает источник.
|
||||
Сервис camera|microphone запускается из видимого приложения после runtime-разрешений; без микрофона — camera.
|
||||
- Camera2 TEMPLATE_RECORD пишет в Surface encoder. Кодеки перечисляются в порядке H.264/H.265/VP9/AV1/H.266;
|
||||
@@ -816,8 +816,9 @@ Android:
|
||||
lookback=200ms, lookahead=0, release=rise*5, target=0.25. История и gain принадлежат только audio worker
|
||||
трансляции; оба канала используют общий gain, сохраняющий стереобаланс. Изменения настроек рации применяются на следующем PCM-кадре, без перезапуска микрофона,
|
||||
Opus или source_epoch; compressor не добавляет задержку и не меняет capture PTS.
|
||||
- Camera/codec callbacks — HandlerThread, Opus encoder — audio capture thread, decoder/AudioTrack — viewer worker.
|
||||
- Native bridge использует общий stream_publisher для TX и собственный actor для viewer.
|
||||
- Camera/codec callbacks — HandlerThread, Opus encoder — audio capture thread, decoder — отдельный viewer worker,
|
||||
общий AudioTrack — worker StreamAudioMixer. Перекладка плиток сохраняет TextureView/decoder.
|
||||
- Native bridge использует общий stream_publisher для TX и stream_viewers для RX.
|
||||
stream_publisher держит pthread mutex, ограниченные очереди и один pending post либо media timer;
|
||||
статистика обслуживается отдельным таймером 100ms. Тот же модуль используется desktop.
|
||||
attach/detach выполняются в uasync вне callbacks. detach отменяет post/timer, закрывает handles,
|
||||
@@ -827,7 +828,8 @@ Android:
|
||||
неполную пачку объединяет с аудио/видео до 20ms; продолжение работы через timer 5ms.
|
||||
При video pressure отбрасываются целые ожидающие кадры, P-кадры запрещены до нового KEY.
|
||||
Кодировщик получает отказ admission и запрашивает KEY с ограничением частоты 500ms.
|
||||
- RX<=4MiB/256 records, bridge готовит один ответ на незавершённый запрос worker.
|
||||
- RX: stream_viewers держит отдельный handle/cursor, demand, status и один owned ответ на session ID.
|
||||
Общей RX FIFO нет: остановка/отставание одного reader не задерживает остальных.
|
||||
Получение ответа и сетевые AVAILABLE не запускают автоматическое чтение следующего кадра.
|
||||
Запас остаётся в RS-кольце. want_video запрашивает следующий полный видеокадр независимо от часов показа;
|
||||
audio_ahead ограничивает только звук относительно PTS потребителя. Управление/звук обслуживаются независимо от video pressure.
|
||||
@@ -842,13 +844,15 @@ Android:
|
||||
- Фиксированного prebuffer нет. Начало clock/audio output разрешено после первого декодированного кадра.
|
||||
AudioTrack задаёт время показа видео по реально услышанному PCM;
|
||||
без аудио используются монотонные часы с остановкой на границе готовых кадров.
|
||||
Android StreamAudioOutput принадлежит viewer worker и хранит один недописанный PCM (до 480 stereo frames).
|
||||
Android StreamAudioOutput принадлежит StreamAudioMixer и хранит один недописанный общий PCM (до 480 stereo frames).
|
||||
Одна WRITE_NON_BLOCKING за проход: частичный/нулевой результат сохраняет остаток, не задерживая video pump.
|
||||
Выдано native = принято устройством + остаток; часы используют playbackHeadPosition без экстраполяции timestamp.
|
||||
Шкала каждого StreamMixedPlayback начинается на своём output frame; playbackHeadPosition переводится
|
||||
в число реально услышанных кадров этого input. PTS разных источников не сравниваются.
|
||||
Заполнение поддерживается до фактического bufferSizeInFrames устройства; API>=31 задаёт start threshold=1.
|
||||
При EOF недописанный PCM передаётся полностью; короткий хвост дополняется не более одного start threshold тишины.
|
||||
Эта тишина не входит в native PTS; завершение ждёт воспроизведения всего принятого PCM, не окончания тишины.
|
||||
Пауза сохраняет остаток; GAP освобождает output вместе с native timeline. Новый output начинает счётчики с нуля.
|
||||
При EOF недописанный PCM передаётся полностью; mixer выдаёт тишину после последнего кадра источника
|
||||
до его физического воспроизведения. END одного input не закрывает AudioTrack остальных.
|
||||
Hold/starvation одного источника добавляет silence span только в его native timeline, не двигая его PTS.
|
||||
GAP создаёт новый input/origin, сохраняя общее устройство и часы остальных трансляций.
|
||||
Watchdog 2s проверяет отсутствие движения hardware head только при достаточном заполнении либо заблокированной записи;
|
||||
намеренная пауза и ожидание данных до порога запуска не считаются зависанием. Ошибка содержит состояние и счётчики устройства.
|
||||
Защитный guard минутного контроллера учитывает audio device lead и наблюдённое опережение decoder
|
||||
@@ -866,7 +870,8 @@ Android:
|
||||
GraphicsLayer преобразует только видео, не Surface/decoder/reader и не управление; жест продлевает таймер скрытия управления.
|
||||
Во время подключения/ошибки управление видно. Смена видимости не меняет геометрию TextureView и не пересоздаёт reader.
|
||||
Выход восстанавливает прежнюю requestedOrientation и видимость системных панелей; ON_STOP освобождает viewer.
|
||||
Собственный источник не открывает viewer/cursor, decoder, AudioTrack и playback controller.
|
||||
При одиночном выборе собственного источника используется прямой preview без reader/decoder.
|
||||
В режиме всех трансляций собственный источник, если активен, также имеет обычную плитку.
|
||||
Preview ориентируется по сенсору камеры и текущему положению экрана; ручной поворот трансляции его не вращает.
|
||||
TextureView уже применяет ориентацию сенсора. UI Matrix компенсирует только -displayDegrees,
|
||||
размеры view учитывают relative rotation; повторно поворачивать на SENSOR_ORIENTATION нельзя.
|
||||
@@ -889,15 +894,18 @@ Android:
|
||||
Камера при закрытии preview продолжает работать; publisher от UI не зависит.
|
||||
Видимый player удерживает экран включённым. Preview ограничен оставшимся местом, кнопки управления не перекрывает.
|
||||
- Source list общий для Android/desktop: stream_sources_json в uasync использует stream_member_load
|
||||
и stream_member_online. stream_watch_sources наблюдает один выбранный канал; group=0 снимает наблюдение.
|
||||
и stream_member_online. stream_watch_sources(inst, group, owner) наблюдает канал от имени владельца;
|
||||
group=0 снимает только его наблюдение. Owner=0 — выбранный чат; desktop-сетка имеет собственного owner.
|
||||
Начальный snapshot и изменения после Merkle/BGP передаются через CHAT_EVT_STREAM_SOURCES=58:
|
||||
group_id:8BE, valid:1, JSON с id(decimal string), epoch(hex string), self, name.
|
||||
События объединяются отложенным callback, одинаковый список повторно не отправляется. Таймера опроса нет.
|
||||
valid=0 сохраняет предыдущий список UI и показывает недоступность; это не пустой успешный снимок.
|
||||
Обнаружение не создаёт подписку, media-буфер и не удерживает ядро в ACTIVE.
|
||||
- В шапке канала значок виден при наличии источников; один источник открывается сразу,
|
||||
несколько выбираются из списка по имени/ID. Пункт меню «Трансляции» также открывает управление публикацией.
|
||||
- В шапке канала значок доступен и при пустом списке; меню содержит «Смотреть все активные трансляции»
|
||||
и выбор отдельного источника по имени/ID. Пункт «Трансляции» также открывает управление публикацией.
|
||||
Выбранная эпоха проверяется actor до подписки и по фактически открытому handle. Перезапуск источника
|
||||
требует нового выбора. Ошибка viewer kind=6 передаёт stream_close_reason в flags.
|
||||
требует нового выбора в одиночном режиме; сетка заменяет только плитку с новой эпохой.
|
||||
Ошибка viewer kind=6 передаёт stream_close_reason в flags.
|
||||
- Ручная ориентация доступна до запуска и во время своей трансляции: три компактные кнопки 0/90/270 градусов относительно
|
||||
обычного положения устройства. Поворот не зависит от текущего положения экрана; SENSOR_ORIENTATION и направление
|
||||
камеры учитываются при расчёте video rotation. Камера/режим/ориентация хранятся и подтверждаются одной конфигурацией.
|
||||
@@ -1016,9 +1024,9 @@ Desktop:
|
||||
Stop закрывает источники и audio callback, join обоих workers, затем FIN и ограниченный drain 2s.
|
||||
Удаление engine происходит до SoundManager и ядра; detach отменяет posts/timers и закрывает publisher
|
||||
в uasync. Протокол uAV2 и wire v5 при добавлении desktop publisher не меняются.
|
||||
- Один StreamPlayerWindow на процесс. Он содержит имя источника, состояние подключения, громкость,
|
||||
полноэкранный режим и закрытие. Выбор нового источника закрывает предыдущего reader.
|
||||
- transport/stream_bridge владеет одним viewer, готовит один ответ на запрос и ограничивает RX 4MiB/16 записей.
|
||||
- Одиночный StreamPlayerWindow либо StreamGridWindow с отдельным player на источник.
|
||||
Player содержит имя, состояние и громкость; fullscreen/close в сетке принадлежат общему окну.
|
||||
- transport/stream_bridge владеет stream_viewers: независимый cursor и один ближайший ответ на reader.
|
||||
Attach/detach выполняются в uasync; команды и копирование медиаданных защищены mutex.
|
||||
Post отменяется до destroy ядра; GUI/decoder не хранят заимствованные instance/handle pointers.
|
||||
- stream_media_cursor собирает ближайший access unit выбранного кодека и выдаёт Opus 48kHz mono/stereo/20ms из RS-кольца.
|
||||
@@ -1027,7 +1035,7 @@ Desktop:
|
||||
Worker держит один рабочий AVPacket и не более двух готовых изображений. Следующий video request не ограничен часами показа.
|
||||
FFmpeg output обслуживается перед input; EAGAIN сохраняет рабочий packet и означает backpressure, не GAP.
|
||||
Дальние кадры и звук не копируются из кольца. SoundTouch и устройство держат только рабочую порцию PCM.
|
||||
Кадры сохраняются до достижения их PTS аудиочасами; при задержке GUI общий clock останавливается.
|
||||
Кадры сохраняются до достижения их PTS аудиочасами; задержка показа удерживает clock только этого player.
|
||||
Видео декодируется без задержки frame-threading. При GAP сбрасываются decoder refs и timeline до KEY.
|
||||
- Первый decoded frame разрешает движение clock/audio, защитный guard учитывает наблюдённый decoder lead.
|
||||
FIN отправляет decoder EOS; выход дренируется теми же ограниченными проходами до EOF, GUI продолжает показ хвоста.
|
||||
@@ -1040,6 +1048,30 @@ Desktop:
|
||||
Закрытие окна снимает только reader, собственная публикация сохраняется.
|
||||
Удаление канала сообщает STREAM_CLOSE_GROUP; остановка ядра снимает media и catalogue owners.
|
||||
- Без FFmpeg окно сообщает, что декодирование недоступно. Нужна desktop-сборка с HAVE_FFMPEG.
|
||||
|
||||
Просмотр всех активных трансляций (desktop и Android):
|
||||
- Добавление/удаление определяется каталогом текущего канала. Порядок существующих плиток стабилен;
|
||||
добавление, удаление, смена эпохи и ошибки одного player не пересоздают остальные.
|
||||
Пропавший источник дочитывает FIN/готовый хвост: дедлайн равен остатку media + 2s, не более 30s.
|
||||
После удаления последней плитки окно остаётся открытым и ждёт новые источники.
|
||||
- Desktop: квадратная сетка ceil(sqrt(count)), в том числе 2x2, 3x3, 4x4; при >16 расширяется дальше.
|
||||
Android выбирает строки/колонки по максимальной площади вписанного видео, без принуждения к квадрату.
|
||||
Плоский keyed Layout сохраняет Surface при перекладке. ON_STOP/закрытие освобождает readers;
|
||||
Surface/SurfaceTexture освобождаются только после выхода decoder worker.
|
||||
Перед Android-звонком новые viewers заблокированы; подготовка ждёт workers и закрытия общего AudioTrack вне UI-потока.
|
||||
- PCM всех источников смешивается в один stereo 48kHz output: SoundManager на desktop,
|
||||
StreamAudioMixer/AudioTrack на Android. Opus, SoundTouch, темп, PTS и A/V clock у каждого свои.
|
||||
Общий limiter имеет быстрый attack и release 100ms; тихая новая плитка не уменьшает громкость остальных.
|
||||
Дополнительного накопительного буфера перед микшером нет; размеры RS-колец не менялись.
|
||||
- Ошибка отдельного decoder повторяется с backoff 0.5..10s; смена эпохи заменяет только этот reader.
|
||||
Закрытие канала/сервиса и удаление участника не вызывают бесконечного retry.
|
||||
Android пробует подходящие декодеры по очереди, включая software при исчерпании аппаратных;
|
||||
непригодный codec/format остаётся с явной ошибкой в своей плитке. Реальная вместимость зависит от устройства.
|
||||
- Проверки: test_stream_view — независимые readers/resync/stop, владельцы каталога, геометрия до 16 плиток,
|
||||
сохранение player при перекладке, новая эпоха, пустое ожидание, два одновременно играющих FFmpeg/Opus player,
|
||||
сумма/limiter общего выхода и независимое отключение input. test_android_stream — независимые native readers.
|
||||
Kotlin StreamAudioMixTest/StreamGridLayoutTest и StreamAudioOutputTest проверяют отдельные origin,
|
||||
частичные записи, сумму PCM и limiter, портретную/альбомную раскладку и вместимость до 40 источников.
|
||||
Проверки: test_stream_view — настоящее ядро, старые эпохи/cancel, задержанное чтение без роста RX, H.264 с поворотом и сменой revision без новой эпохи,
|
||||
Opus/громкость на настоящем null audio backend, непрерывные 30fps при физической audio latency 200ms,
|
||||
закрытие канала и полный баланс tracked allocations. Раз в секунду desktop viewer логирует decode/show FPS,
|
||||
|
||||
@@ -4,6 +4,19 @@
|
||||
|
||||
## Текущие задачи
|
||||
|
||||
[+] **Просмотр всех активных трансляций: desktop, затем Android** — 20.10.2026:
|
||||
Пункт в меню канала открывает автоматически обновляемую сетку; пустое окно ждёт новые источники.
|
||||
Desktop: 2x2/3x3/4x4 с расширением по числу плиток; Android: раскладка по доступной площади экрана.
|
||||
Общий stream_viewers хранит независимые handles/cursors; перекладка не пересоздаёт действующие players.
|
||||
Отдельные Opus/SoundTouch/PTS/аудиочасы каждого источника, сумма PCM в одном output с limiter.
|
||||
GAP, завершение и ошибка одного источника не останавливают остальные; размеры RS-колец не менялись.
|
||||
C/desktop и Android APK собраны; Qt stream_view 19/19, шесть stream/audio recovery целей и native Android PASS.
|
||||
Android unit 129/129; check.sh 164/165: прежний encrypted_attachment phase=5/heartbeats=12001.
|
||||
На физических телефонах многопоточный просмотр пока не проверен; сборки не устанавливались.
|
||||
Контракты и ограничения: doc/stream.txt. Логи: /tmp/multiview-desktop-audit-test.log,
|
||||
/tmp/multiview-desktop-final-tests.log, /tmp/multiview-android-native-final.log,
|
||||
/tmp/multiview-android-final.log, /tmp/multiview-full-check.log.
|
||||
|
||||
[+] **Multicast с исходящими дескрипторами и входящими подписками** — 20.10.2026:
|
||||
Отдельный routing_layer/multicast.c; контракт и примеры использования — multicast.h.
|
||||
Номера 1..65535 выдаются последовательно без поколений; подписки на (группа, source, descriptor).
|
||||
|
||||
Reference in new issue
Block a user