Видеозвонки Android и ChatGUI Контракт Каждая сторона независимо включает видео. Автовыбор H.265 Main 8-bit, затем H.264 Constrained Baseline: пересечение encoder отправителя и decoder получателя. Камера, разрешение, кодек и верхняя граница битрейта меняются во время звонка. Время Общая монотонная шкала захвата аудио и видео на каждом отправителе. PTS первого аудиосэмпла/кадра сохраняется до вывода; время сетевой отправки учитывается отдельно. Приёмное видео следует исходной позиции реально воспроизводимого аудио, включая SoundTouch и очередь устройства. Алгоритм регулирования аудиобуфера сохраняется. Искусственная тишина/тоны не продвигают исходную позицию. Формат видео меняется по PTS; новое поколение не уничтожает старые кадры, соответствующие ещё не прозвучавшей речи. Передача и регулирование Один A/V-планировщик, независимые логические каналы управления/аудио/видео. Звук продолжает работу при отказе камеры. Ограничены ожидание encode, очереди, размер сборки кадра и память получателя; ACK не равен успешному decode/render. Роутер считает исходный payload и пакеты, видеомодуль вычитает фиксированный заголовок фрагмента. Физические очереди повторно не суммируются с router inflight. Окно ACK: 10 завершённых интервалов по 50мс, текущая ячейка отдельно. Регулятор общий C, решения проверяются управляемым временем. Снижение учитывает подтверждённую скорость и разгрузку задолженности; повышение — малыми пробами. Нет отдельного неограниченного интегратора: задолженность уже интегрирует дефицит. Учитываются прогрев, RTT, minrtt_probe, отсутствие маршрута, локальное удержание планировщиком, применение encoder-команды и смена эпохи. Владение Состояние звонка/регулятора/роутера — uasync. Камера и encoder/decoder — платформенные workers. Ограниченный mailbox, одно ожидающее уведомление. Команды bitrate имеют call_id, video_generation и request_id; одно исполнение и последнее желаемое значение. Принятая настройка не заменяет измерение фактического выхода. Shutdown отменяет уведомления/таймеры, останавливает producers, затем освобождает очереди и codecs. Порядок реализации [+] История первых ACK 500мс/50мс и снимок payload send_q/inflight отдельного сервиса. [+] Unit счётчиков: повторные ACK, reset, отдельные сервисы; UDP/STCP регрессии пройдены. [+] Дрейф feed/tempo устранён картой фактических участков SoundTouch; см. ниже. [ ] H.265 runtime bitrate: поддерживаемый backend без циклического пересоздания. [+] Общий регулятор с детерминированными сценариями (пока не подключён к звонку). [ ] Протокол возможностей/формата, видеофрагменты, сборка, A/V scheduler. [+] Общие capture PTS и отображение позиции аудиоустройства в исходное время. [ ] ChatGUI: движок, окно и настройки; Android: движок, JNI, сервис и экран. [ ] Интеграционные проверки всех пар, перегрузки, смены формата и lifecycle. Приёмка Нет двойного учёта байтов: принято = подтверждено + отменено + задолженность. Повторные ACK/передачи не увеличивают delivered. Reset не считается доставкой. Свободный канал не вызывает снижение на статичной сцене; рост полосы обнаруживается пробами. Очереди остаются ограниченными при отсутствии ACK и остановке decoder. Видео следует аудио при догоне, underrun, смене устройства и формате видеотрека. Завершение звонка безопасно во время переключения камеры, bitrate и decoder. Проверка SoundTouch, 14.10.2026 test_soundtouch_clock сравнивает положение реальных четырёхсэмпловых фрагментов в выходном сигнале с двумя вариантами временной шкалы. 60с входа, mono 48kHz, sequence=20ms, seek=10ms, overlap=4ms — параметры существующего call_jitter. Максимальная ошибка feed/tempo / consumed-input, мс: постоянный темп 1.6: 14.208 / 14.271 плавный цикл 1.0..1.6 (шаг 0.012/20ms): 29.550 / 13.723 стресс, переключение 1.0/1.6 каждые 20ms: 938.104 / 14.462 Стресс не воспроизводит ограничение темпа звонка, а проверяет отсутствие накопления. Проблема: setTempo меняет обработку ещё не потреблённого входа; номинальные интервалы frames/tempo, записанные при feed, не описывают фактический выход. Поэтому копировать этот способ из stream_playback_audio в звонки нельзя. Изменений звука/буфера нет. Рассмотренные варианты (реализован вариант 2): 1. Минимальный: шкала по фактически потреблённому входу и произведённому выходу, с поправкой на seek-window. Тест показал <15мс на трёх сценариях, но это ещё не доказанная граница для речи/Opus/разных блоков. Нужны эти проверки и явный допуск. 2. Более строгий: SoundTouch публикует соответствие исходных участков выходным непосредственно в TDStretch. Позволяет учитывать выбранный WSOLA offset без вычисления по косвенным счётчикам; потребует небольшого расширения bundled API. Overlap смешивает два участка, поэтому даже здесь нужно определить правило PTS. Рекомендуется для общего A/V clock: не меняет звук, исключает накопительный дрейф. Реализованная шкала SoundTouch сообщает Copy/Mix с фактическими индексами входа/выхода после WSOLA. Mix интерполирует PTS двумя теми же линейными весами, которыми смешивается PCM. Это определение времени смешанного сигнала, а не утверждение, что у смеси один источник. Поддержан профиль звонка: float PCM, mono/stereo, rate=pitch=1, AA выключен. Неподдержанная смена профиля/flush отключает карту до reset, сохраняя обработку звука. CallTimeline переносит участки через частичные receiveSamples и локальные тоны/тишину. Кольца ограничены; переполнение или противоречивый progress запрещают valid до reset. Краткое движение исходного PTS назад удерживается, held_back_us видно в диагностике. Разрыв более 100мс и смешивание разных source_epoch не дают valid-PTS. Аудиопакет несёт capture_pts_us, capture_epoch и codec_delay_samples (Opus lookahead). Метка неизвестного источника — INT64_MIN. Старого формата протокола больше нет. После разрыва захвата хвост прежнего Opus не получает вымышленную новую метку. Desktop передаёт исходную метку callback захвата; playback использует позицию PCM и время вывода backend. measured показывает аппаратную отметку либо оценку backend. Android использует AudioRecord timestamp MONOTONIC; до появления timestamp оценка привязана один раз к окончанию первого read и далее идёт по сэмплам. После исчезновения timestamp продолжается последний якорь. Это ограничение точности, а не аппаратное измерение. AudioTrack playbackHeadPosition разворачивается из unsigned32 и ограничивается реально записанным PCM. Опрос head — estimated (measured=0), write не подтверждает воспроизведение. Локальные часы API — MONOTONIC, не get_time_us ядра (на Linux он использует BOOTTIME). Снимок без progress более 250мс недостоверен; экстраполяции за подтверждённый участок нет. Логи call DEBUG показывают epoch/source/played/written/PTS/valid/measured/held раз в секунду. Видеопротокол, codecs, scheduler и UI ещё не подключены. Проверка A/V на устройствах остаётся следующим интеграционным этапом; unit-тесты не измеряют физическую задержку динамика. Проверки текущего этапа ./build.sh --no-gui -j4 — успешно. ./check.sh — 153/153, proxy 9/9, burst и load успешно; /tmp/call-video-check.log. test_etcp_router_unit — 36/36; /tmp/call-video-router-unit.log. UDP и STCP — по 440 пакетов, 5 циклов backpressure, без потерь/дублей/повреждений; /tmp/call-video-router-udp.log, /tmp/call-video-router-tcp.log. Регулятор: модель 120с с полосой 1.5→3Mbps, max debt=118559 bytes; статичная сцена — 0 ложных снижений. Pending, cap, no route, warmup, локальное удержание, minrtt probe, RTT=600ms проверены. Интеграция encoder ещё отсутствует. test_soundtouch_clock — три сценария проходят предел consumed-input <20ms; этот предел теста не является гарантией для всех входных сигналов. Проверки реализованного варианта 2 Сравнение SoundTouch с callback и без него: PCM побитно одинаков; Copy/Mix восстанавливаются по фактическому входу, включая mono/stereo, partial read и clear. CallTimeline: 200с реального SoundTouch, смена темпа, границы участков, переполнение, неверная/устаревшая позиция устройства и восстановление через reset. CallAudio: 40с реального Opus+jitter, burst/догон, тоны/тишина, эпоха устройства; capture metadata проверены через очередь uasync и реальный сетевой звонок. Desktop: шесть профильных + пять проверок общего аудиостека прошли, vibechat собран. Android: assembleDebug и все 79 unit-тестов прошли (arm64-v8a и x86_64 native). ./check.sh: 153/153, proxy 9/9, burst/load PASS; /tmp/call-clock-check-final.log. Логи: /tmp/call-clock-desktop-final-tests.log, /tmp/call-clock-shared-tests.log (test_radio_member_mute повторён вне sandbox: /tmp/call-clock-radio-mute-test.log), /tmp/call-clock-android-final.log. Физические устройства в этом этапе не проверялись.