Browse Source

Test capture clock diagnostics and document routing ownership

master
evgeny 5 days ago
parent
commit
d944418676
  1. 20
      doc/chat_call_arch.md
  2. 20
      tools/chatgui/tests/test_audio_diagnostics.cpp

20
doc/chat_call_arch.md

@ -129,6 +129,13 @@ capture/play потоки независимо создают и запуска
Ошибка любого направления завершает звонок. Общий `stop()` отменяет подготовку, разблокирует I/O и ждёт оба потока
до освобождения Android-устройств и native-аудиостека. Отдельные locks защищают подготовку/остановку каждого устройства.
Замер AEC планируется через 300мс после готовности обоих направлений и не задерживает воспроизведение.
При наличии Telecom Connection режимом и выходом звонка управляет только Telecom.
ACTIVE запрашивается до создания аудиоустройств; AudioManager/прямой preferred device не дублируют выбор.
На API 34+ маршрут запрашивается через доступный CallEndpoint, на старых — через Connection.setAudioRoute.
Подтверждённый маршрут приходит из callback системы, отдельно от желания пользователя; ошибка запроса логируется.
Если Connection ещё нет, используется AudioManager. Поздняя регистрация передаёт управление Telecom,
снимая заявку приложения на режим и прямые устройства. Рация сохраняет отдельный AudioManager-путь.
Реальное время запуска/переключения зависит от Android и требует проверки на устройстве.
Маршрутизатор использует один снимок входных/выходных устройств; обновляет его при старте,
событиях устройств/BT-профиля и явной переоценке. По завершении снимок освобождается.
INFO-логи Android показывают длительности настройки режима, регистрации callback, BT-профиля, выбора маршрута,
@ -146,7 +153,18 @@ INFO-логи Android показывают длительности настро
Это очередь AudioTrack, а не полная сквозная задержка и не сетевой jitter reserve.
Ранее на Samsung startFrames=1920 был принят, но без ограничения рабочего буфера
written-played оставался 120–160мс; одного стартового порога недостаточно.
Фактически принятый размер после изменения требует проверки на устройстве, включая смену маршрута.
Замер Samsung подтвердил постоянную очередь 40мс. При прежнем параллельном управлении маршрутом
AudioTrack пересоздавался, очередь временно достигала 160мс, а ожидание свободного места — 328–336мс.
При старте AudioTrack.play занимал 1080мс. Новое владение маршрутом нужно проверить тем же сценарием.
На desktop `AudioDiag: capture clock` при call=debug выводит секундную сводку исходных backend-блоков:
число блоков/сэмплов, накопленную последовательность, диапазон ошибки относительно конца предыдущего блока,
latency PulseAudio, число скачков >2мс, hole-сэмплы и блоки без достоверного измерения.
Первый скачок за секунду содержит обе latency/поправки конвертера, expected/actual PTS и монотонное время.
Backend публикует только числовой снимок в ограниченное кольцо; форматирование выполняет reporter.
Это наблюдение, а не коррекция: пороги 2мс в call_audio и 20мс в VoiceAudioIo пока сохранены.
Счётчик sequence описывает полученные backend-сэмплы; отсутствие потерь очереди само по себе
не доказывает непрерывность физического захвата. Проверять вместе с hole/overflow и route-событиями.
Диагностика Android при `call=debug`: playback сохраняет один числовой снимок на блок (20мс)
в кольце 512 элементов, без форматирования/вывода. Отдельный reporter раз в 200мс забирает записи;

20
tools/chatgui/tests/test_audio_diagnostics.cpp

@ -19,6 +19,26 @@ int main() {
assert(take(e)); assert(e.value == int64_t(i) && e.detail == 7);
}
assert(!take(e)); lost = 0;
// Граница секундного отчёта не обрывает карту времени; hole отделён от скачка оценки latency.
std::map<uintptr_t, Device> devices;
audio_capture_timing timing{1000000, 100000, 0, 480, 48000, 1, 0};
audio_diag_capture_clock(nullptr, &timing);
timing.first_us = 1014000; timing.latency_us = 96000;
audio_diag_capture_clock(nullptr, &timing);
timing.first_us = 1024000; timing.missing = 1;
audio_diag_capture_clock(nullptr, &timing);
while (take(e)) consume(e, devices);
auto& capture = devices[0];
assert(capture.captureSequence == 1440 && capture.captureFrames == 1440 && capture.captureBlocks == 3);
assert(capture.captureJumps == 1 && capture.captureMaxDelta == 4000 && capture.captureMinDelta == 0);
assert(capture.captureMissing == 480 && capture.captureEstimated == 0);
assert(capture.captureMinLatency == 96000 && capture.captureMaxLatency == 100000);
report(devices, now(CLOCK_MONOTONIC), 1.0);
timing.first_us = 1034000; timing.missing = 0; timing.measured = 0;
audio_diag_capture_clock(nullptr, &timing);
assert(take(e)); consume(e, devices);
assert(capture.captureJumps == 0 && capture.captureMissing == 0 && capture.captureEstimated == 1);
assert(capture.captureSequence == 1920 && capture.captureBlocks == 1 && capture.captureFrames == 480);
std::vector<std::thread> workers;
for (int p = 0; p < producers; ++p) workers.emplace_back([p, &done]() {
for (int i = 0; i < perProducer; ++i)

Loading…
Cancel
Save