Document lossless recovery contract and verify actual audio source rate

This commit is contained in:
evgeny committed 2026-10-19 17:40:51 +02:00
1 parent a9b91145a6
commit b7ae7ef700
2 files changed
+57 -2

No files matched your search

+55 -2
View File
@@ -688,7 +688,7 @@ Samsung удерживает кадр/следующие encoded frames до н
test_call_video_clock exit=0, test_call_video_encoder 13/13 PASS. Диагностика установлена на Samsung,
ChatGUI пересобран и перезапущен. Поведение кодеков, очередей, аудиосинхронизации не изменено.
Независимое расписание показа, 15.10.2026
Независимое расписание показа, 15.10.2026 (история; заменено восстановлением без пропусков ниже)
Общий C-модуль call_video_playout принадлежит RX worker, не владеет изображениями/decoder.
Последовательные сроки: D[i] = D[i-1] + (PTS[i]-PTS[i-1])*25/26.
Локальная received_us ставится на входе RX port в CLOCK_MONOTONIC до ожидания worker;
@@ -872,7 +872,7 @@ test_call_video проверяет сочетания PPS flags/extra header bit
Новое требование: сохранять последовательность encoded кадров, быстро декодировать пришедший
burst и плавно догонять показом. Короткая пауза не должна вызывать пропуск входного P-кадра,
ожидание следующего KEY или перескок на более свежее изображение.
Текущая реализация этому требованию не соответствует; в этой итерации добавлена диагностика,
На момент аудита реализация этому требованию не соответствовала; сначала добавлена диагностика,
алгоритм буферизации/показа и действующие ограничения не изменены.
1. Router сохраняет порядок, при заполнении RX port приостанавливает доставку/ACK и возобновляет
@@ -920,3 +920,56 @@ Windows-диагностика: AudioDiag теперь работает чере
время исполнения/интервалы; video RX worker показывает decode и опоздание пробуждения.
video late drop содержит точные PTS/AV/возраст первого позднего кадра за окно, playout —
диапазоны всех late кадров. Измерения не корректируют Windows callback-estimate аудиочасы.
Восстановление без пропусков, 19.10.2026 — действующее поведение
Encoded-кадр после принятия сохраняет ID/PTS и владельца до передачи дальше. TX port
удерживает непринятый хвост пачки и повторяет его при уведомлении writable. Новая пачка
не принимается во время обработки старой. Busy=0 не меняет references и не запрашивает KEY;
ошибка цепочки/формата/отказ decoder возвращает -1 и диагностируется отдельно.
Admission ограничивает новые raw-входы encoder, но не запрещает передачу уже готовых кадров.
Router send_q будит flush через отменяемый waiter; audio progress, CAPS ACK и timer — другие источники wake.
Mailbox: до восьми кадров, каждый <=2MiB; максимальная пачка <=16MiB без предварительного выделения.
Transport сохраняет прежние пределы 32 кадра/500мс PTS/2MiB; RX — 8 кадров/4MiB.
Это верхние границы ёмкости, заполнение не является условием начала передачи/показа.
Полный RX FIFO удерживает BUSY до разгрузки, создавая паузу production для устранения долга.
Обычный поток не проходит этот hysteresis. Длительная перегрузка/обрыв ограничены deadline головы TX 2с:
после него цепочка явно сбрасывается с причиной deadline и запросом KEY, память не растёт бесконечно.
GPU/MediaCodec отдают готовые выходы независимо от новых кадров камеры. При backpressure
подача raw останавливается до encode; уже принятые GPU-входы продолжают drain в ограниченную очередь.
При смене bitrate сначала полностью забираются выходы старого encoder, затем запускается новый KEY.
Фоновая подготовка desktop сохраняется. Некорректный первый KEY — настоящая ошибка encoder,
а не busy; выводится ошибка и отключается подача вместо бесконечного повторения неисправного режима.
Явные stop/смена режима/отсоединение Surface отменяют соответствующие старые очереди.
Decoder получает кадры строго последовательно, без искусственных интервалов 30fps между decode.
Готовые изображения остаются ограничены двумя; Android inputs+outputs — восемью.
Основной backlog хранится encoded, а не большим RGB FIFO или удержанием всех аппаратных outputs.
Playout больше не содержит DROP_LATE/DROP_SCHEDULE. Invalid/order остаётся диагностируемой ошибкой.
CallTimeline публикует rate_ppm из фактического SourceSpan SoundTouch -> PCM на текущей позиции устройства.
База темпа видео max(1x, audio rate), коррекция 2 * max(0, audioPTS-framePTS) ppm, предел 1.6x.
После показа следующий интервал равен deltaPTS/speed; после опоздания отсчитывается от фактической
выдачи, поэтому очередь не выдаётся мгновенной пачкой. После отсутствия готовых кадров привязка снимается:
прошедшее ожидание данных не добавляется второй раз. Дополнительный стартовый резерв не создаётся.
Видео может временно отставать от звука более 50мс, затем догоняет; опережение ограничено 40мс.
Невалидные аудиочасы приостанавливают показ, сохраняют decoder и порядок кадров.
Desktop сохраняет один queued кадр до paintEvent acknowledgement; serial защищает stop/restart.
Скрытое/свёрнутое окно принимает поток без ожидания paint, физический показ там не обещается.
Android выпускает следующий кадр только после чтения соответствующего SurfaceTexture timestamp
после RenderThread commit (API29+); на API26–28 проверка выполняется через postOnAnimation.
Проверяется Surface identity; detach/stop отменяет ожидание. Commit/paint не измеряют scanout панели.
Диагностика call=debug: playout speed_ppm/AV/cadence, RX decode/wake, GUI notification/paint wait,
Android awaitingSurfacePts/surfaceWaitMs. TX paused/resumed и RX full/drained показывают границы давления.
Windows WASAPI padding/latency остаются наблюдениями; аппаратная привязка аудио Windows не изменялась.
Для проверки реального Windows тракта нужны обновлённая сборка и новый звонок с call=debug.
Проверки: test_call_video_playout сохраняет 480/480 кадров при паузах сети/CPU 50/100/200/500мс,
аудиодогоне и недостоверных часах; отдельно моделируется poll с шагом 15.625мс (не замер Windows).
test_call_video_port проверяет частично принятую пачку, повторный busy, отсутствие KEY/spin и RX drain.
test_call_video_flush проверяет bounded burst, событие send_ready, аудиогейт и отмену callbacks.
Qt clock требует 90/90 при RX/GUI-паузах; GPU-тест проверяет каждый исходный PTS через смену bitrate
и равенство decoded + pending входам, без настоящего Windows драйвера.
@@ -20,8 +20,10 @@ static void test_boundaries() {
map.progress(1, 960, 20000, 20000, true);
auto clock = map.clock(20000);
CHECK(clock.valid && clock.pts_us == 1010000 && clock.written_samples == 1920);
CHECK(clock.rate_ppm == 1000000);
map.progress(1, 1200, 25000, 25000, true);
CHECK(map.clock(25000).pts_us == 1010000); // Локальная тишина.
CHECK(map.clock(25000).rate_ppm == 0);
map.progress(1, 1920, 40000, 30000, true);
clock = map.clock(35000);
CHECK(clock.valid && clock.pts_us == 1015000 && clock.played_samples == 1680);