Browse Source

Document audio trace fields and verify diagnostic reads preserve jitter events

master
evgeny 4 days ago
parent
commit
ac2029c82b
  1. 21
      doc/chat_call_arch.md
  2. 8
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioTrace.kt
  3. 10
      tools/chatgui/tests/test_call_jitter.cpp

21
doc/chat_call_arch.md

@ -140,6 +140,27 @@ INFO-логи Android показывают длительности настро
(на desktop 60–80мс). Лог `playback buffering` показывает принятый порог и ёмкость;
ежесекундный `clock` — queuedUs, текущий порог и underruns, прирост underruns имеет уровень WARN.
Это очередь AudioTrack, а не полная сквозная задержка и не сетевой jitter reserve.
Повторный замер: startFrames=1920 принят, но written-played остался 140мс; одного стартового порога недостаточно.
Диагностика Android при `call=debug`: playback сохраняет один числовой снимок на блок (20мс)
в кольце 512 элементов, без форматирования/вывода. Отдельный reporter раз в 200мс забирает записи;
раз в секунду выводит `audio summary` с min/avg/max очередей, задержек pull/write и опоздания потока,
целевым запасом/разбросом доставки JB, темпом, недостающим PCM и отдельными счётчиками JB/AudioTrack.
Снимок JB не сбрасывает события для CALL_STATS. encodedMs — очередь Opus после pull,
processingMs — оставшийся PCM SoundTouch в длительности исходной речи; reserveMs зависит только от доставки.
AudioTimestamp опрашивается раз в 200мс, его frame/time и время опроса печатаются отдельно;
при недоступности стоит stampOk=0. Времена trace — MONOTONIC; очередь устройства — written-played.
Эти показатели нельзя складывать с возрастом AudioTimestamp как независимые задержки.
`audio detail` сохраняет 2с до и 3с после старта, фактической смены routedDevice или новой аномалии:
очередь устройства >100мс в течение секунды, отсутствие продвижения head 100мс при наличии данных,
либо новый episode underrun (затухает через 3с без повторений). Порог очереди — диагностический,
не настройка JB и не изменение воспроизведения. События внутри окна объединяются, между выгрузками
не менее 10с; продолжающаяся аномалия не создаёт повторных дампов. Восстановление логируется INFO.
Подробные строки содержат по четыре снимка и заголовок с порядком колонок; при stop окно может быть неполным.
Переполнение кольца при задержке reporter имеет отдельный WARN lost=N. Аудиоданные не сохраняются.
Выключение call=debug прекращает сбор/выгрузку; буфер и reporter освобождаются с playback worker.
Сравнивать сводки следует по call ID и monoUs; рост сетевого запаса сам по себе не является ошибкой.
## 7. Буфер звонка и восстановление

8
tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioTrace.kt

@ -29,6 +29,8 @@ internal class CallAudioTrace(private val id: Long, private val emit: (String, S
private var stoppedSince = -1L
private var underrunAt = -10_000_000L
private var faults = 0
private var reportedDeviceUnderruns = 0L
private var reportedJbUnderruns = 0L
@Synchronized fun record(row: LongArray) {
if (!enabled) return
@ -51,6 +53,8 @@ internal class CallAudioTrace(private val id: Long, private val emit: (String, S
if (!enabled) {
history.clear(); summary.clear(); previous = null; eventAt = -1
faults = 0; highSince = -1; stoppedSince = -1; underrunAt = -10_000_000
lastDump = -10_000_000; suppressed = 0
reportedDeviceUnderruns = 0; reportedJbUnderruns = 0
return
}
if (overflow > 0) emit("WARN", "audio trace call=$id reporter overrun lost=$overflow")
@ -108,10 +112,12 @@ internal class CallAudioTrace(private val id: Long, private val emit: (String, S
"route=${last[1]}/${last[2]} min/avg/max queueUs=${range(::queued)} " +
"jbMs=${range { it[16] + it[17] }} reserveMs=${range { it[18] }} spreadMs=${range { it[19] }} " +
"pullUs=${range { it[5] }} writeUs=${range { it[6] }} wakeLateUs=${range { it[7] }} " +
"tempo=${last[21]} deviceUnderruns=${last[9]} jbUnderruns=${last[22]} " +
"tempo=${last[21]} deviceUnderruns=${last[9]} deviceUnderDelta=${last[9] - reportedDeviceUnderruns} " +
"jbUnderruns=${last[22]} jbUnderDelta=${last[22] - reportedJbUnderruns} " +
"zeroWrites=${summary.sumOf { it[8] }} missingPcm=${summary.sumOf { (960 - it[20]).coerceAtLeast(0) }} " +
"stamp=${last[12]}@${last[13]} readUs=${last[14]} ok=${last[15]} " +
"buffer=${last[10]} start=${last[11]} fault=$faults suppressed=$suppressed")
reportedDeviceUnderruns = last[9]; reportedJbUnderruns = last[22]
summary.clear()
}

10
tools/chatgui/tests/test_call_jitter.cpp

@ -122,6 +122,16 @@ int main() {
CHECK(call_jitter_pull(j, pcm, 480) == 0);
push(j, 2);
CHECK(call_jitter_pull(j, pcm, 480) > 0);
call_jitter_diagnostics diagnostic{};
call_jitter_get_diagnostics(j, &diagnostic, 0);
CHECK(diagnostic.resumed == 1 && diagnostic.pcm_samples == 480 && diagnostic.reserve_ms == 60);
CHECK(diagnostic.encoded_ms >= 0 && diagnostic.processing_ms >= 0 && diagnostic.arrival_spread_ms == 0);
call_jitter_get_diagnostics(j, &diagnostic, 0);
CHECK(diagnostic.resumed == 1); // Снимок playback не потребляет события для uasync.
call_jitter_get_diagnostics(j, &diagnostic, 1);
CHECK(diagnostic.resumed == 1);
call_jitter_get_diagnostics(j, &diagnostic, 0);
CHECK(diagnostic.resumed == 0);
call_jitter_range range;
for (int i = 3; i < 503; i++) push(j, i, 10060); // 10с сетевой задержки, затем пачка
call_jitter_get_stats(j, nullptr, nullptr, nullptr, nullptr, &range);

Loading…
Cancel
Save