Browse Source

Cover immediate buffer probe failures and document tuning

master
evgeny 4 days ago
parent
commit
91030de5ea
  1. 31
      doc/chat_call_arch.md
  2. 26
      tools/chatgui-android/app/src/test/java/com/utun/chat/data/CallPlaybackBufferTest.kt

31
doc/chat_call_arch.md

@ -141,8 +141,8 @@ ACTIVE запрашивается до создания аудиоустройс
INFO-логи Android показывают длительности настройки режима, регистрации callback, BT-профиля, выбора маршрута,
создания/старта устройств, первого чтения микрофона и первой записи в AudioTrack, а также готовность другого направления.
Общий `call_audio` логирует время ожидания первого PCM; запись в AudioTrack может содержать начальную тишину.
Эффективный рабочий буфер AudioTrack на всех поддерживаемых API запрашивается размером
два блока PCM (40мс); на API 31+ отдельно задаётся такой же стартовый порог.
Эффективный рабочий буфер AudioTrack на всех поддерживаемых API при старте запрашивается размером
два блока PCM (40мс); на API 31+ стартовый порог следует фактически принятому размеру.
Ёмкость выделенной памяти может быть больше. Ограничение повторяется по callback фактической
смены выхода на main, под trackLock; callback старого AudioTrack игнорируется и снимается при stop.
Подачу PCM задаёт свободное место в AudioTrack, без дополнительного таймера 20мс.
@ -155,7 +155,9 @@ INFO-логи Android показывают длительности настро
written-played оставался 120–160мс; одного стартового порога недостаточно.
Замер Samsung подтвердил постоянную очередь 40мс. При прежнем параллельном управлении маршрутом
AudioTrack пересоздавался, очередь временно достигала 160мс, а ожидание свободного места — 328–336мс.
При старте AudioTrack.play занимал 1080мс. Новое владение маршрутом нужно проверить тем же сценарием.
При старте AudioTrack.play занимал 1080мс. После перехода к единственному владельцу Telecom
в проверенном звонке старт занял 62мс, очередь оставалась не более 40мс и при смене выхода.
Переключение всё ещё останавливало потребление на 293–328мс; этот всплеск принимает сетевой JB.
На desktop `AudioDiag: capture clock` при call=debug выводит секундную сводку исходных backend-блоков:
число блоков/сэмплов, накопленную последовательность, диапазон ошибки относительно конца предыдущего блока,
@ -172,12 +174,23 @@ Backend публикует только числовой снимок в огр
Счётчик sequence описывает полученные backend-сэмплы; отсутствие потерь очереди само по себе
не доказывает непрерывность физического захвата. Проверять вместе с hole/overflow и route-событиями.
Дальнейший подбор минимальной очереди аудиоустройства (ещё не реализован) отделён от JB/SoundTouch:
старт с проверенного размера, редкие попытки уменьшения после стабильного участка, увеличение при
подтверждённом backend underrun, пауза обучения при смене маршрута/запуске. Размер запрашивается
аппаратными шагами; сохраняется фактически принятый размер, а не только запрос.
Ожидание полного буфера и недостача сети не являются поводом увеличить очередь устройства.
На Android регулируется AudioTrack без flush/recreate; на desktop выход принадлежит SoundManager,
На Android `CallPlaybackBuffer` подбирает рабочую очередь отдельно от JB/SoundTouch:
после 30с наблюдаемого продвижения playback head без underrun пробует уменьшить её на один
блок PCM (960 сэмплов, 20мс), до одного блока или фактического минимума устройства.
При росте AudioTrack.underrunCount добавляет один блок в пределах выделенной ёмкости.
Интервал следующей пробы удваивается (60/120/240/300с), чтобы не повторять частые неудачные уменьшения.
После старта, запроса/подтверждения маршрута и увеличения даются 2с на стабилизацию;
после уменьшения первый underrun учитывается немедленно. Отсутствие продвижения head или
пропуск наблюдений >1с обнуляет стабильный участок. Сброс системного счётчика учитывается отдельно.
Фактическая смена выхода начинает подбор с 40мс заново. Непринятая меньшая заявка фиксирует
нижнюю границу до следующего выхода; ошибки API выключают подбор с WARN/ERROR, сохраняя воспроизведение.
Reporter управляет размером под trackLock независимо от фильтра debug; playback не берёт этот lock.
Нет flush/recreate, частичная запись PCM сохраняется. После смены размера/выхода через секунду
повторно измеряется задержка для AEC. Ожидание полного буфера и недостача сети не увеличивают его размер.
Лог настройки содержит reason, previous/requested/actualFrames и прирост underruns;
достижение ёмкости предупреждает не чаще раза в 10с. Принятие и стабильность 20мс ещё требуют
проверки на Samsung; буферы HAL и Bluetooth могут добавлять собственную задержку.
На desktop адаптация устройства ещё не реализована: выход принадлежит SoundManager,
изменение backend-буфера должно выполняться его владельцем, а не отдельным звонком.
Диагностика Android при `call=debug`: playback сохраняет один числовой снимок на блок (20мс)

26
tools/chatgui-android/app/src/test/java/com/utun/chat/data/CallPlaybackBufferTest.kt

@ -63,7 +63,7 @@ class CallPlaybackBufferTest {
assertEquals("stable-probe", d.tick()?.reason)
}
@Test fun AcceptedHardwareMinimumIsNotProbedForever() {
@Test fun acceptedHardwareMinimumIsNotProbedForever() {
val d = Device(minimum = 1440)
d.run(32_000)
assertEquals(1440, d.frames)
@ -113,4 +113,28 @@ class CallPlaybackBufferTest {
d.run(32_000)
assertEquals(960, d.frames)
}
@Test fun firstUnderrunImmediatelyAfterShrinkRejectsProbe() {
val d = Device()
d.run(32_000)
assertEquals(960, d.frames)
d.underruns++
assertEquals("underrun", d.tick()?.reason)
assertEquals(1920, d.frames)
}
@Test fun repeatedFailuresBackOffToFiveMinutesButStillAllowRecovery() {
val d = Device()
d.run(32_000)
val delays = listOf(60_000L, 120_000L, 240_000L, 300_000L, 300_000L)
for (delay in delays) {
d.underruns++
assertEquals("underrun", d.tick()?.reason)
assertEquals(1920, d.frames)
d.run(delay + 1_800)
assertEquals(1920, d.frames)
assertEquals("stable-probe", d.tick()?.reason)
assertEquals(960, d.frames)
}
}
}

Loading…
Cancel
Save