|
|
|
|
@ -703,3 +703,83 @@ END, принятый от первого поставщика, сообщает
|
|
|
|
|
Restart в той же source_epoch сохраняет consumer cursor: bootstrap от отстающего поставщика не повторяет уже выданные DATA. |
|
|
|
|
Повтор restart до отложенного GAP сохраняет предыдущую, ещё не сообщённую приложению границу; пропуск сообщается одним GAP. |
|
|
|
|
Инструкция и параметры стенда: tests/stream_netns/README.md. |
|
|
|
|
|
|
|
|
|
8. Live media и Android |
|
|
|
|
|
|
|
|
|
Транспортный wire v3 не меняется. stream_publish_block публикует одну непустую FEC-пачку |
|
|
|
|
немедленно, с сохранением границ. Первая запись выбирает режим источника: byte write или block write; |
|
|
|
|
смешивание отвергается до изменения данных. stream_publish_finish не освобождает буфер источника. |
|
|
|
|
|
|
|
|
|
Медиаблок uAV1 находится целиком внутри входа RS, поэтому header восстанавливается вместе с данными. |
|
|
|
|
При штатных 6+2 полный медиаблок <=7080 байт. Раскладка big-endian, без packed-структур: |
|
|
|
|
|
|
|
|
|
fixed header (32 байта): |
|
|
|
|
magic="uAV1":4, video_codec:1, audio_codec:1, video_count:1, audio_count:1, |
|
|
|
|
width:2, height:2, rotation:2, header_size:2, video_size:4, audio_size:4, |
|
|
|
|
first_video_frame_offset:4, config_revision=1:4. |
|
|
|
|
Далее video_count описаний по 32 байта, audio_count описаний по 16 байт, |
|
|
|
|
затем video_size видеобайтов и audio_size аудиобайтов. Описания задают последовательные |
|
|
|
|
неперекрывающиеся диапазоны внутри соответствующего трека; сумма длин должна совпадать с header. |
|
|
|
|
|
|
|
|
|
video descriptor: |
|
|
|
|
frame_id:8, PTS_us:8, frame_size:4, fragment_offset:4, fragment_length:4, flags:1, reserved=0:3. |
|
|
|
|
audio descriptor: |
|
|
|
|
PTS_us:8, packet_length:4, samples:2, reserved=0:2. |
|
|
|
|
|
|
|
|
|
first_video_frame_offset относительно начала video bytes: UINT32_MAX — начала нет, |
|
|
|
|
иначе это смещение первого фрагмента с fragment_offset=0. Начал может быть несколько, |
|
|
|
|
таблица описывает каждое. Данные конфигурации не являются отдельным кадром. |
|
|
|
|
KEY=bit0 video flags; остальные биты запрещены. frame_id>0, <=16 описаний каждого трека. |
|
|
|
|
Один H.264 кадр <=2MiB. Общий формат допускает медиаблок <=64KiB; реальный максимум дополнительно |
|
|
|
|
ограничен входом RS. Конфигурация, разрешение, поворот и набор треков фиксированы на source_epoch. |
|
|
|
|
|
|
|
|
|
video_codec=1: AVC/H.264 Annex B, без B-кадров. Каждый KEY содержит SPS, PPS и IDR в одном |
|
|
|
|
логическом кадре; это позволяет позднему viewer настроить декодер после получения полного KEY. |
|
|
|
|
audio_codec=0: нет аудио; audio_codec=1: Opus, 48000Hz mono, 960 samples (20ms), packet<=1275 bytes. |
|
|
|
|
Opus-пакеты между медиаблоками не разрезаются. Размеры треков не включают header/описания. |
|
|
|
|
|
|
|
|
|
stream_media проверяет весь блок до изменения view. Receiver собирает один последовательный кадр. |
|
|
|
|
При GAP, потере продолжения или конфликте metadata недособранный кадр удаляется, видео ждёт KEY. |
|
|
|
|
Аудио обрабатывается независимо; неверный формат/длины не передаются платформенным декодерам. |
|
|
|
|
Ретрансляторы не разбирают uAV1 и не пересоздают подписи источника. |
|
|
|
|
|
|
|
|
|
Android: |
|
|
|
|
- Один foreground publisher на процесс, один viewer. Каждая операция имеет новый session ID. |
|
|
|
|
- StreamService владеет камерой, микрофоном и PARTIAL_WAKE_LOCK; пересоздание Activity не завершает источник. |
|
|
|
|
Сервис camera|microphone запускается из видимого приложения после runtime-разрешений; без микрофона — camera. |
|
|
|
|
- Camera2 TEMPLATE_RECORD пишет в Surface AVC encoder. Выбирается совместимое разрешение <=1280x720, |
|
|
|
|
30fps, AVC baseline, 1–2Mbit/s, I-frame interval=1s. Выход нормализуется в Annex B; |
|
|
|
|
partial output собирается в access unit. Codec config передаётся с каждым IDR. |
|
|
|
|
- Camera MediaCodec Surface timestamps и AudioTimestamp.TIMEBASE_MONOTONIC приводятся к общему |
|
|
|
|
origin System.nanoTime; время callback/получения блока не заменяет capture PTS. |
|
|
|
|
- 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. |
|
|
|
|
UI не сохраняет borrowed inst/ua. Idle без pending data не будит мобильное ядро. |
|
|
|
|
- TX<=2MiB/128 records, не более 250ms ожидания. Actor публикует <=2 пачек за вызов, |
|
|
|
|
неполную пачку объединяет с аудио/видео до 20ms; продолжение работы через timer 5ms. |
|
|
|
|
При video pressure отбрасываются целые ожидающие кадры, P-кадры запрещены до нового KEY. |
|
|
|
|
Кодировщик получает отказ admission и запрашивает KEY с ограничением частоты 500ms. |
|
|
|
|
- RX<=4MiB/256 records. Pressure очищает media, сохраняет control results и сообщает GAP. |
|
|
|
|
Kotlin viewer дополнительно ограничивает video queue 12 кадрами/2MiB, audio queue 16 пакетами. |
|
|
|
|
Нельзя выбросить reference P-кадр и продолжить с зависимыми: переполнение сбрасывает декодер до KEY. |
|
|
|
|
Просроченный кадр при нормальной последовательности декодируется без показа. |
|
|
|
|
- После prebuffer 150ms AudioTrack clock определяет время показа видео; видео без аудио использует |
|
|
|
|
монотонные часы. Короткие audio gaps заполняются Opus PLC, большой разрыв сбрасывает timeline. |
|
|
|
|
Локальный preview использует обычную подписку на собственный источник с отключённым воспроизведением |
|
|
|
|
своего микрофона, чтобы исключить акустическую обратную связь. Камера при закрытии preview продолжает работать. |
|
|
|
|
- Source list строится в uasync по stream_member_load и stream_member_online, выдаётся UI копией JSON. |
|
|
|
|
Свежий snapshot содержит source ID, epoch, self и экранированное имя. Увиденный источник не создаёт подписку. |
|
|
|
|
- Stop сначала завершает capture producers и encoder EOS (максимум 1s), затем FIN. |
|
|
|
|
Publisher сохраняет конечный буфер ещё 2s для сетевой выдачи, затем закрывается. |
|
|
|
|
Это ограниченный drain, не подтверждение воспроизведения всеми viewers. Abort/core restart очищает очередь сразу. |
|
|
|
|
Поздние команды старого session ID не меняют новый запуск. |
|
|
|
|
- Звонок, рация и voice recording не начинают новый capture во время трансляции; |
|
|
|
|
трансляция также отвергается при активном звонке/рации/voice recording. |
|
|
|
|
|
|
|
|
|
Логи: stream — epoch/owners, очереди, GAP и teardown; video — codec/config, KEY, PTS и декодер. |
|
|
|
|
Kotlin progress — DEBUG раз в секунду; C packet-level диагностика — TRACE по запросу. |
|
|
|
|
Проверки: 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 и усечённые данные). |
|
|
|
|
|