1437 lines
163 KiB
Plaintext
1437 lines
163 KiB
Plaintext
Видеозвонки Android и ChatGUI
|
||
|
||
Контракт
|
||
Каждая сторона независимо включает видео. Автовыбор H.265 Main 8-bit, затем
|
||
H.264 Baseline/Constrained Baseline: пересечение encoder отправителя и decoder получателя.
|
||
Encode/decode проверяются независимо, профили AVC согласуются явной маской.
|
||
HEVC Main с Main/High tier (High начиная с level 4), 8-bit 4:2:0, progressive, без reorder; DPB ограничен Annex A,
|
||
не фиксированным числом references. Общий call_video_bitstream проверяет VPS/SPS/PPS,
|
||
HRD при наличии, геометрию и независимый IDR. Фактический level проверяется после
|
||
открытия encoder. CAPS — 64 байта, header — 56 байт; старый формат не поддерживается.
|
||
VBV является локальной настройкой software encoder, в сетевом контракте его нет.
|
||
Результат запуска decoder (generation + accepted) передаётся отдельно от router ACK:
|
||
неподдержанный формат вызывает fallback в Auto, потеря references — запрос KEY.
|
||
Камера, разрешение, частота до 60 fps, кодек и диапазон битрейта меняются во время звонка.
|
||
Автоматический диапазон по умолчанию 300–4000 кбит/с (десятичные единицы).
|
||
|
||
Время
|
||
Общая монотонная шкала захвата аудио и видео на каждом отправителе. PTS первого
|
||
аудиосэмпла/кадра сохраняется до вывода; время сетевой отправки учитывается отдельно.
|
||
Приёмное видео следует исходной позиции реально воспроизводимого аудио, включая
|
||
SoundTouch и очередь устройства. Алгоритм регулирования аудиобуфера сохраняется.
|
||
Показ имеет собственное абсолютное расписание с плавным темпом по суммарному запасу до 1.6x;
|
||
audio clock ограничивает опережение videoPTS-audioPTS до +40мс. Устойчивый долг при ограничении
|
||
частоты экрана снимается пропуском только показа уже декодированных кадров.
|
||
Подробности и границы измерения — в разделе «Независимое расписание показа» ниже.
|
||
Искусственная тишина/тоны не продвигают исходную позицию. Формат видео меняется по 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.
|
||
|
||
Применение bitrate на ключевом кадре (уточнение пользователя)
|
||
Worker хранит pending-команду и применяет её перед кодированием ближайшего
|
||
планового IDR. GOP ограничен одной секундой исходной шкалы capture PTS, а не
|
||
числом принятых сетью кадров: backpressure не должен блокировать переключение.
|
||
Для x265 используется новый encoder с нужным bitrate; обычный reconfigure даже
|
||
на IDR не обходит найденный дефект 4.1. Исправленная внешняя библиотека не нужна.
|
||
Первый выход нового encoder обязан содержать IDR и актуальные VPS/SPS/PPS.
|
||
Камера, audio epoch, capture PTS и последовательность frame_id не перезапускаются.
|
||
При неизменном формате bitrate не меняет video_generation; новые parameter sets
|
||
идут с KEY. Смена codec/разрешения/камеры сохраняет отдельный протокол generation.
|
||
Приём применяет конфигурацию перед декодированием соответствующего KEY, не удаляя
|
||
уже декодированные старые кадры, ожидающие своего времени по audio clock.
|
||
Подтверждение rate_applied отправляется после проверки первого нового IDR,
|
||
независимо от сетевого admission; иначе закрытый admission создаст взаимное ожидание.
|
||
Успех означает применение настройки, не гарантию бит-в-бит постоянного выхода.
|
||
До IDR регулятор видит старый applied bitrate и одну pending-команду; его текущий
|
||
таймаут 12с включает ожидание IDR, проверку hardware encoder (до 5с) и возможное
|
||
восстановление предыдущей конфигурации (до 5с). При отсутствии кадров/ошибке
|
||
команда завершается отказом; нельзя бесконечно продлевать её таймаут.
|
||
Ограничитель очереди работает и во время ожидания. Если новый IDR не принят,
|
||
producer запрашивает следующий IDR по существующему контракту send_frame.
|
||
Desktop готовит кандидата в отдельном потоке, продолжая кодирование старым encoder.
|
||
Готовый кандидат применяется только на IDR; при незавершённой подготовке текущий
|
||
GOP продолжается. Android сначала меняет bitrate через MediaCodec.setParameters
|
||
без остановки; подтверждает только проверенный KEY с PTS не раньше команды.
|
||
Уже закодированные P-кадры перед этим KEY сохраняются: runtime bitrate не разрушает references.
|
||
При отказе runtime API Android переоткрывает encoder последовательно (аппаратный
|
||
ресурс может быть единственным), при ошибке восстанавливает прежнюю конфигурацию
|
||
и подтверждает фактически восстановленный bitrate. В этой резервной ветке возможна
|
||
пауза картинки; аудио и capture timeline продолжаются.
|
||
|
||
Порядок реализации
|
||
[+] История первых ACK 500мс/50мс и снимок payload send_q/inflight отдельного сервиса.
|
||
[+] Unit счётчиков: повторные ACK, reset, отдельные сервисы; UDP/STCP регрессии пройдены.
|
||
[+] Дрейф feed/tempo устранён картой фактических участков SoundTouch; см. ниже.
|
||
[+] ChatGUI codec component: замена H.264/H.265 encoder на IDR, с непрерывными PTS.
|
||
[+] Android MediaCodec: применение bitrate на ближайшем IDR, проверка фактического формата.
|
||
[+] Общий регулятор с детерминированными сценариями и подключением к router/encoder-командам.
|
||
[+] Видеофрагменты, ограниченная сборка, обмен возможностями с подтверждением.
|
||
[+] Согласование profiles/level и целевого bitrate; проверка transport/encoder, независимый decoder probe.
|
||
[+] A/V scheduler с платформенными очередями захвата/encode.
|
||
[+] Общие capture PTS и отображение позиции аудиоустройства в исходное время.
|
||
[+] ChatGUI: движок, окно и настройки; Android: движок, JNI, сервис и экран.
|
||
[ ] Интеграционные проверки всех пар, перегрузки, смены формата и lifecycle.
|
||
|
||
Приёмка
|
||
Нет двойного учёта байтов: принято = подтверждено + отменено + задолженность.
|
||
Повторные ACK/передачи не увеличивают delivered. Reset не считается доставкой.
|
||
Свободный канал не вызывает снижение на статичной сцене; рост полосы обнаруживается
|
||
пробами. Очереди остаются ограниченными при отсутствии ACK и остановке decoder.
|
||
Видео следует аудио при догоне, underrun, смене устройства и формате видеотрека.
|
||
Завершение звонка безопасно во время переключения камеры, bitrate и decoder.
|
||
|
||
Далее журнал этапов разработки. Старые описания wire/profile/VBV ниже исторические;
|
||
актуальный контракт приведён в начале, последние проверки — в конце документа.
|
||
|
||
Проверка 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. Физические устройства в этом этапе не проверялись.
|
||
|
||
Реализованный видеотранспорт, 14.10.2026
|
||
call_video.c задаёт общий wire и сборку; call_video_transport.c владеет состоянием
|
||
видеоканала внутри call_session. Сигнализация звонка, video control, audio и video
|
||
имеют отдельные router service IDs. Приёмная очередь видео не блокирует аудио.
|
||
CAPS повторяется до прикладного CAPS_ACK: получение транспортного ACK не гарантирует,
|
||
что получатель уже обработал ACCEPT и установил capabilities. Кодек выбирается
|
||
направленно: encoder отправителя пересекается с decoder получателя, H.265 перед H.264.
|
||
Текущие limits проверяют размеры, FPS и pixels/second; контракт профиля ещё неполон.
|
||
|
||
Заголовок фрагмента 52 байта, payload до 1200 байт; полный кадр до 2MiB.
|
||
KEY содержит config_length:4BE + AnnexB config + access unit. Ядро проверяет
|
||
границы envelope; содержимое NAL обязан проверять платформенный decoder.
|
||
frame_id и capture PTS возрастают на всём протяжении звонка, включая смену generation.
|
||
Новый формат требует нового generation и KEY. После пропуска кадра нужен KEY.
|
||
На TX ограниченная FIFO: до 32 кадров, 500мс по PTS и 2MiB суммарно; на RX одна ограниченная сборка.
|
||
Deadline головы TX и сборки RX — 2с. Каждый TX кадр ждёт соответствующего audio_end_pts;
|
||
следующие P-кадры сохраняются в FIFO, а не теряются на обычной задержке аудиообработки.
|
||
При потере головы отбрасывается вся зависимая цепочка и запрашивается KEY. В сетевой долг
|
||
входят только готовые к отправке кадры FIFO; ожидание аудио само по себе не перегружает канал.
|
||
receive_ready резервирует возможность принять целый кадр до ACK первого фрагмента.
|
||
Callback копирует данные до возврата; платформенная очередь также должна быть ограничена.
|
||
Старые кадры другого generation сохраняются до соответствующей позиции аудиоряда.
|
||
|
||
Видео пока удерживается до передачи соответствующего аудио в router. Аудио никогда
|
||
не ждёт камеру. Это первая часть scheduler: ограничение ожидания encode, pacing
|
||
на максимальном битрейте и политика остановки видеотрека остаются следующими задачами.
|
||
flush ограничен 16 фрагментами за вызов; пропускную способность этого бюджета нужно
|
||
проверить вместе с частотой вызовов от платформенного producer и аудио.
|
||
|
||
Регулятор получает endpoint ACK 500мс/50мс и queued+inflight отдельного видеосервиса.
|
||
Фиксированные заголовки вычитаются; добавляется ещё не переданный остаток кадра.
|
||
Удержание по аудио не трактуется как нехватка сети. Смена эпохи сбрасывает окно.
|
||
Ограничение admission: задолженность + новый кадр не превышают 2MiB.
|
||
Одна команда bitrate в работе, подтверждение проверяет generation/request;
|
||
устаревшее подтверждение отвергается. Timeout 2с или отказ encoder останавливает
|
||
admission до явной повторной настройки; аудиозвонок продолжает работу.
|
||
Платформенные callbacks вызываются в uasync и не меняют call API реентерабельно.
|
||
При hangup отменяются таймеры и освобождаются pending TX/RX, callbacks снимаются.
|
||
|
||
Проверки видеотранспорта
|
||
./check.sh: 154/154, proxy 9/9, burst/load PASS; /tmp/call-video-core-check.log.
|
||
test_call_video проверяет capabilities, wire, фрагментацию, replay, разрыв,
|
||
timeout, смену generation и некорректные размеры/config.
|
||
test_call через два реальных UDP-ядра проверяет обмен CAPS, удержание видео по аудио,
|
||
backpressure получателя без остановки аудио, H.265 -> H.264 с новым generation,
|
||
bitrate command/ACK, устаревшие ACK и hangup с ожидающим кадром.
|
||
Payload в этом тесте синтетический: тест не доказывает работу HEVC/AVC decoder.
|
||
Лог: /tmp/call-video-rate-integration.log.
|
||
ChatGUI vibechat собран; test_voice_tx и test_call_jitter прошли.
|
||
Android assembleDebug/testDebugUnitTest успешны; /tmp/call-video-android-core-build.log.
|
||
Камера, декодирование/показ и физическая A/V-синхронизация пока не проверены.
|
||
|
||
Выявленная нестыковка перед подключением codecs
|
||
Размеров/FPS/pixels-per-second недостаточно: допустимый битрейт зависит также от
|
||
profile/level/tier и ограничений конкретного decoder. Текущий StreamVideoEncoder
|
||
не умеет runtime bitrate для H.265, а прямой x265 требует дополнительной проверки.
|
||
В upstream x265_encoder_reconfig при смене profile/level/tier отменяет изменение
|
||
rate-control, но эта ветка не меняет ret=0. Успешный return не доказывает применение.
|
||
Источник: https://github.com/videolan/x265/blob/master/source/encoder/api.cpp
|
||
|
||
Предлагаемое продолжение:
|
||
1. Зафиксировать Main 8-bit / Constrained Baseline и согласовать level/tier,
|
||
верхний bitrate/VBV и ограничения reference frames для каждого направления.
|
||
Выбранный режим должен проверяться фактическим открытием encoder/decoder.
|
||
2. Открывать encoder в согласованных пределах. Автоадаптация работает внутри них
|
||
без пересоздания. ACK команды означает проверенное фактическое применение,
|
||
а не только возврат setter; отдельно измеряется реальный выход encoder.
|
||
3. Пользовательское изменение за пределами режима создаёт новый generation,
|
||
config и KEY после проверки возможностей получателя; звук продолжается.
|
||
4. Проверить установленный x265 и Android MediaCodec тестом encode/decode с
|
||
понижением/повышением битрейта и отказом настройки. Непригодный H.265 backend
|
||
не объявляется доступным; в auto используется H.264.
|
||
Подключение платформенных codecs остановлено на этой границе согласно AGENTS.md.
|
||
|
||
Настройка диапазона, 14.10.2026
|
||
ChatGUI: Настройки -> аудио, поля минимального/максимального битрейта видеозвонка.
|
||
Android: Настройки -> аудио, ползунок диапазона. По умолчанию 300–4000 кбит/с;
|
||
пользователь может сузить диапазон внутри этих границ, включая фиксированный bitrate.
|
||
Одна атомарная настройка call_video_bitrate_range_kbps=300:4000 хранится в конфиге
|
||
desktop / DataStore Android и передаётся в общий chat_setting. min > max отвергается
|
||
без частичного изменения. Android включает настройку в конфиг при каждом запуске.
|
||
Во время звонка transport читает диапазон каждые 50мс, не сбрасывая pending-команду.
|
||
Верхняя граница ограничена проверенным codec cap текущего generation. Если новый
|
||
минимум выше codec cap, admission приостановлен до совместимой настройки/режима;
|
||
это событие логируется. Смена диапазона сама по себе не расширяет возможности codec.
|
||
При сети слабее 300 кбит/с target не уходит ниже нижней границы; bounded admission
|
||
не позволяет очереди бесконечно расти. Аудиоканал продолжает работать отдельно.
|
||
Порог 5% при снижении не мешает достигнуть точной нижней границы диапазона.
|
||
|
||
Проверено: модель полосы 150 кбит/с -> 6 Мбит/с достигает target 300 кбит/с,
|
||
затем 4 Мбит/с, очередь остаётся ограниченной. Сетевой test_call меняет диапазон
|
||
при pending-команде, принимает прежний ACK и затем получает target нового диапазона;
|
||
также проверяет отказ несовместимого codec cap. Логи /tmp/call-range-rate-test.log
|
||
и /tmp/call-range-integration-test.log. ChatGUI собран; Android assembleDebug и
|
||
81 unit-тест прошли, /tmp/call-range-android-ui-final.log.
|
||
Полный ./check.sh вне sandbox: 154/154, proxy 9/9, burst/load PASS;
|
||
/tmp/call-range-check-final.log. Первый запуск в sandbox блокировался запретом сокетов.
|
||
Настройки и регулятор готовы; подключение камеры/codec/render остаётся отдельным этапом.
|
||
|
||
Проверка реального x265 перед подключением backend, 14.10.2026
|
||
Добавлена ручная цель probe_call_x265 (EXCLUDE_FROM_ALL, не входит в ctest), если
|
||
найден x265 через pkg-config. Сборка/запуск:
|
||
cmake --build tools/chatgui/build --target probe_call_x265 -j4
|
||
./tools/chatgui/build/probe_call_x265
|
||
./tools/chatgui/build/probe_call_x265 --frame-rc
|
||
Один encoder, 640x360@30, Main 8-bit/Main tier/level 3.1, reference=1,
|
||
B=0/lookahead=0, ABR+VBV, буфер 1000 кбит. Три участка по 120 кадров:
|
||
4 Мбит/с -> 300 кбит/с -> 4 Мбит/с. Детерминированная шумовая сцена требует полосы;
|
||
измерение каждого участка исключает первые 30 кадров переходного процесса.
|
||
Код 0 — измерение прошло; 1 — ошибка API/потеря PTS; 2 — непригодный rate control.
|
||
Допуск измерения 0.5..1.5 target — диагностический, не гарантия CBR для любой сцены.
|
||
|
||
Установленная библиотека 4.1+1-1d117be (пакет 4.1-4) не проходит пробу:
|
||
target 4000000: output 4016866 бит/с;
|
||
target 300000: output 4006669 бит/с, несмотря на getter bitrate=300/vbv=300 кбит/с;
|
||
target 4000000: output 3945016 бит/с.
|
||
Лог: /tmp/call-x265-system.log. Return reconfigure=0 и getter недостаточны.
|
||
frame-rc также непригоден для этого zerolatency-профиля: output около 40 кбит/с
|
||
на всех трёх участках; /tmp/call-x265-frame-rc.log.
|
||
|
||
Причина проверена по исходникам именно установленного выпуска:
|
||
https://deb.debian.org/debian/pool/main/x/x265/x265_4.1.orig.tar.xz
|
||
SHA256 85abb8a850000bf693947459c42a666a3ecd6de10a2ac6b3c6647554b14d3270
|
||
Encoder::configure(p) теперь присваивает this->m_param=p. Вызов из
|
||
x265_encoder_reconfig с m_latestParam переключает encoder на новый объект, но
|
||
RateControl::m_param остаётся прежним адресом. Последующий x265_copy_params
|
||
копирует latest в него самого; reconfigureRC читает старые параметры rate control.
|
||
Для frame-rc обнаружен отдельный дефект: tuneQscaleToUpdatedBitrate делит
|
||
frameBitsTotal на i=0 при пустом lookahead, что в этой пробе приводит к QP=69.
|
||
|
||
Проверенный минимальный патч сохраняет активный m_param до configure(latest)
|
||
и восстанавливает его после. Файл для дальнейшего подключения:
|
||
tools/chatgui/ffmpeg/patches/x265-4.1-reconfigure-param-owner.patch
|
||
Применён только к отдельной копии исходников в /tmp/call-x265-source;
|
||
библиотека собрана в /tmp/call-x265-build без установки в систему.
|
||
С тем же тестом и frame-rc=off output=4016866 / 298165 / 4213269 бит/с.
|
||
Логи: /tmp/call_x265_probe_fixed.log и /tmp/call-x265-fixed.log.
|
||
Дополнительная проверка ffprobe: 360/360 кадров декодируются, Main, level=93
|
||
(HEVC level 3.1), has_b_frames=0. Системная библиотека и приложение не заменены.
|
||
ChatGUI успешно собирается; /tmp/call-x265-desktop-build.log.
|
||
|
||
Первоначальное предложение (заменено решением переключать encoder на IDR ниже):
|
||
закрепить проверенную версию x265 и патч в локальной
|
||
сборке зависимости, повторить codec-пробу для рабочих разрешений и decoder,
|
||
затем подключить direct API к backend звонка. Непрошедший проверку backend
|
||
не объявлять в capabilities; H.264 остаётся альтернативой, когда H.265 недоступен.
|
||
Патч пока не подключён к build_ffmpeg.sh; изменение поставки зависимости и
|
||
интеграция backend остановлены по правилу AGENTS.md о выявленной нестыковке.
|
||
|
||
Проверка переключения на плановом IDR, 14.10.2026
|
||
По уточнению пользователя вместо поставки изменённой x265 проверено пересоздание
|
||
encoder на очередном IDR. Добавлены режимы probe_call_x265:
|
||
--keyframe-reconfig
|
||
--keyframe-reopen --output /tmp/call-x265-key-reopen.hevc
|
||
Запрос приходит на кадре 7 очередного участка; применение перед кадром 30,
|
||
через 23 кадра (767мс при 30fps). Плановый IDR задаётся каждые 30 кадров,
|
||
проверяются его NAL-тип и наличие VPS/SPS/PPS. PTS всех 360 кадров проверяются
|
||
на точное соответствие исходным, включая обе границы замены encoder.
|
||
Измеряются последние 60 кадров каждого 120-кадрового участка, после перехода.
|
||
На системной x265 4.1 без патча:
|
||
reconfigure перед IDR: 4008828 / 4001940 / 3977828 бит/с — отказ, код 2;
|
||
новый encoder на IDR: 4008828 / 309564 / 4019812 бит/с — PASS, код 0.
|
||
Логи: /tmp/call-x265-key-reconfig.log, /tmp/call-x265-key-reopen.log.
|
||
ffmpeg -v error -xerror декодирует объединённый поток без ошибок;
|
||
ffprobe подтверждает Main/level 3.1, B=0 и 360 кадров.
|
||
Это проверка 640x360@30 на синтетической сложной сцене, не готовый тракт звонка:
|
||
worker/camera/decoder UI и Android MediaCodec ещё предстоит подключить и проверить.
|
||
Патч x265 остаётся диагностическим материалом и не требуется выбранному способу.
|
||
|
||
Компонент кодирования ChatGUI, 14.10.2026
|
||
StreamVideoEncoder переименован в LiveVideoEncoder и выделен в chatgui_live_video;
|
||
трансляции используют тот же код, без дублирования захвата/конвертации/FFmpeg.
|
||
Явный профиль H.264 Constrained Baseline / H.265 Main задаёт level, reference=1,
|
||
VBV и 8-bit 4:2:0. Для этого режима пока проверены software libx264/libx265.
|
||
Открытие проверяет независимый IDR настоящим decoder: profile, level, Main tier,
|
||
pixel format, отсутствие B-кадров и размер. Проверка принятого option не заменяет
|
||
эту проверку bitstream. Automatic-профили трансляций сохраняют прежний выбор GPU/SW.
|
||
Опции encoder сверены с https://ffmpeg.org/ffmpeg-codecs.html#libx265 и разделом libx264.
|
||
|
||
CallVideoEncoder принадлежит video worker. Он хранит единственную pending
|
||
команду generation/request/bitrate, отклоняет replay, чужой generation и выход за
|
||
согласованный maximumBitrate. После команды кандидат открывается в фоне;
|
||
на ближайшем IDR после готовности проверяется полный KEY с точным исходным PTS.
|
||
Старый encoder сохраняется до успешной проверки; stop ждёт завершения подготовки.
|
||
При отказе кандидата возвращается completion с bitrate=0 и используется прежний
|
||
контекст. Неизменный target также подтверждается на KEY без пересоздания.
|
||
Completion отделён от отправки кадра: даже при закрытом admission его нужно
|
||
передать в call_video_rate_applied. Периодический IDR задаётся по capture PTS,
|
||
поэтому пропуски кадров/закрытая сеть не сдвигают применение на дополнительные GOP.
|
||
Физический захват камеры, audio origin и сетевые frame_id компонентом не меняются.
|
||
|
||
test_call_video_encoder прошёл: 5 QtTest cases (включая init/cleanup), без skip.
|
||
Для обоих codecs: 640x360@30, level 3.1, VBV 1 Мбит, шумовая сцена;
|
||
запрос на кадре 7, переход на кадре 30, один decoder на все 360 кадров.
|
||
Проверены PTS каждого decoded frame, профиль/level/размер/B=0 и средний bitrate:
|
||
H.264: 3996916 / 296904 / 3989748 бит/с;
|
||
H.265: 3993720 / 302956 / 3999788 бит/с.
|
||
Отдельно проверены отказ новой конфигурации с сохранением старого encoder,
|
||
предел bitrate, pending/replay/generation, force-key и разрыв capture PTS вперёд.
|
||
Логи: /tmp/call-live-video-test-final.log, /tmp/call-live-video-build-final.log.
|
||
test_stream_publish: 11 passed, 1 skip (capture под Xvfb), 0 failed;
|
||
/tmp/call-live-stream-regression.log. После уточнения VBV профильная проверка
|
||
liveSoftwareBitrateKeepsDecoder повторена успешно, /tmp/call-live-stream-bitrate-final.log.
|
||
ChatGUI собран; ./check.sh: 154/154, proxy 9/9, burst/load PASS за 118с;
|
||
/tmp/call-live-core-check.log. Android в этом этапе не менялся.
|
||
|
||
Граница этого этапа: рабочий codec component, ещё не камера/worker/видеозвонок.
|
||
Следующим этапом реализовано согласование ограничений, описанное ниже.
|
||
|
||
Согласование ограничений codec, 14.10.2026
|
||
Общий C-протокол фиксирует H.264 Constrained Baseline / H.265 Main, Main tier,
|
||
8-bit 4:2:0, progressive, reference=1, B=0. Другие профили этим протоколом не объявляются.
|
||
CAPS отдельно для encode/decode каждого codec передаёт width/height/FPS,
|
||
pixels_per_second, maximum_bps, maximum_vbv_bits и максимальный level.
|
||
Размер CAPS теперь 80 байт, заголовка фрагмента 60 байт; старый формат не поддерживается.
|
||
|
||
call_video_choose пересекает encode отправителя с decode получателя и выбирает
|
||
H.265, затем H.264. Явный codec запрещает автоматическую замену. Выбирается минимальный
|
||
level, вмещающий весь запрошенный режим, включая максимальный bitrate и VBV.
|
||
Неподходящий запрос не меняется и не сокращается молча; более узкий диапазон требует
|
||
нового явного выбора владельцем. Таблицы проверяют размер, частоту, геометрию и BR/CPB.
|
||
AVC учитывает дополнение до макроблоков 16x16; HEVC консервативно резервирует дополнение
|
||
до CTU 64x64 независимо от backend. Это может выбрать level выше минимального для
|
||
конкретного encoder. Пределы BR/CPB взяты консервативно для VCL без повышающих NAL-факторов.
|
||
Сверено с таблицами FFmpeg и исходниками x265 4.1:
|
||
https://raw.githubusercontent.com/FFmpeg/FFmpeg/master/libavcodec/h264_levels.c
|
||
https://raw.githubusercontent.com/FFmpeg/FFmpeg/master/libavcodec/h265_profile_level.c
|
||
|
||
CAPS задаёт независимые верхние границы: не гарантируется работоспособность всех
|
||
их сочетаний на конкретном устройстве. Перед объявлением и использованием нужны
|
||
платформенные пробы encoder/decoder. Проверка структуры формата не заменяет проверку
|
||
фактических parameter sets и кадров декодером.
|
||
|
||
call_video_format содержит level, maximum_bps и vbv_bits. Эти поля, codec и геометрия
|
||
неизменны внутри generation; изменение требует нового generation и KEY.
|
||
Текущий bitrate меняется на IDR в пределах этого формата без нового generation.
|
||
call_video_configure_rate получает весь формат, проверяет обе CAPS и сохраняет его;
|
||
TX обязан передавать именно этот формат, RX также проверяет согласованные ограничения.
|
||
Дополнительный потолок регулятора может быть ниже уже применённого bitrate, чтобы
|
||
дождаться его снижения, но applied не может превышать maximum_bps самого формата.
|
||
Изменение пользовательского диапазона не переписывает согласованный формат.
|
||
|
||
Если новые CAPS перестали вмещать текущий формат, admission закрывается, ожидающий
|
||
TX-кадр отбрасывается с причиной capabilities_changed и запрашивается новый KEY.
|
||
CallVideoEncoder открывается прямо из call_video_format; дублирующего описания
|
||
режима между общим протоколом и desktop encoder больше нет. Проверка кандидата
|
||
реальным decoder и сохранение старого контекста при отказе остаются обязательными.
|
||
Диагностика CAPS выводит level/bitrate/VBV каждой стороны; отклонённый TX показывает
|
||
frame_id/PTS относительно предыдущего кадра, размер/offset и валидность формата.
|
||
|
||
Проверки: test_call_video покрывает направленный выбор, fallback, границы level,
|
||
bitrate/VBV, атомарность отказа и смену generation. test_call: 2/2 PASS, включая
|
||
сужение CAPS при ожидающем кадре и завершение звонка; /tmp/call-envelope-network-fixed.log.
|
||
Реальный codec-тест: 5/5 QtTest cases PASS. Для 640x360@30 общий выбор даёт level 3.0
|
||
(AVC 30 / HEVC 90), максимум 4 Мбит/с, VBV 1 Мбит. На 360 кадрах с одним decoder:
|
||
H.264: 3996048 / 296904 / 3989748 бит/с;
|
||
H.265: 3993720 / 302956 / 3999788 бит/с.
|
||
PTS, профиль и level проверены на всех участках; /tmp/call-envelope-codec-test.log.
|
||
Итоговый ./check.sh: 154/154, proxy 9/9, burst/load PASS за 128с;
|
||
/tmp/call-envelope-full-check-final.log. ChatGUI и Android assembleDebug собраны:
|
||
/tmp/call-envelope-gui-build-verified.log, /tmp/call-envelope-android-build-final.log.
|
||
|
||
Интеграция платформ и аппаратная проверка, 14.10.2026
|
||
Добавлен общий call_video_port: один TX mailbox, RX до восьми кадров/4MiB,
|
||
команды формата/bitrate с ID звонка, отменяемый post, безопасный detach ядра.
|
||
Сетевой test_call проверяет обе стороны port, владение RX, backpressure TX,
|
||
изменение диапазона и отмену ожидающего post: 2/2 PASS; /tmp/call-video-port-network.log.
|
||
ChatGUI: отдельные TX/RX workers, Qt camera, FFmpeg decoder с проверкой выхода,
|
||
очередь показа по call_audio_get_clock. Окно содержит камеру, разрешение, codec,
|
||
диапазон bitrate и видео. Сборка успешна; production decoder проверен на прежних
|
||
360 кадрах каждого codec с переходами bitrate, 5/5 QtTest cases PASS:
|
||
/tmp/call-video-desktop-integration-build.log, /tmp/call-video-decoder-test.log.
|
||
|
||
Android: JNI к общему port, Camera2/ImageReader YUV, смена MediaCodec на IDR без
|
||
перезапуска камеры при смене bitrate, RX Surface decoder по аудиочасам с drain
|
||
старого generation. CallService владеет видео и camera foreground; CallScreen
|
||
содержит управление/разрешение камеры/Surface. AssembleDebug успешен, установлено
|
||
на D2AR10P3600820 и RZ8R72MG64B. Полный видеозвонок ещё НЕ принят и не проверен.
|
||
Фактический VBV аппаратного encoder также пока не подтверждён: MediaCodec не даёт
|
||
универсального управления CPB, принятие configure не доказывает этот предел.
|
||
|
||
Аппаратный блокер подтверждён явной debug instrumentation CallVideoProbe:
|
||
adb -s SERIAL shell am instrument -w com.utun.chat/com.utun.chat.data.CallVideoProbe
|
||
Samsung RZ8R72MG64B: c2.qti.avc.encoder проходит реальный encode/decode при явном
|
||
AVCProfileConstrainedBaseline. c2.qti.hevc.encoder/OMX.qcom.video.encoder.hevc:
|
||
SPS profile=1, tier=1, level=93 при запросе Main tier (первые байты 42 01 01 21).
|
||
D2AR10P3600820: c2.unisoc.avc.encoder/OMX.sprd.h264.encoder возвращают SPS
|
||
profile=66, constraints=128, level=31, а не Constrained Baseline; явный запрос
|
||
AVCProfileConstrainedBaseline этого не меняет. HEVC Main/Main tier/level=93
|
||
имеет sps_max_dec_pic_buffering_minus1=2 (три decoded pictures), reorder=0.
|
||
Текущий Android validator требует DPB<=2; это более жёсткое требование, чем
|
||
reference=1: размер DPB нельзя автоматически приравнивать числу активных references.
|
||
Логи: /tmp/call-video-probe2-D2AR10P3600820.log,
|
||
/tmp/call-video-probe2-RZ8R72MG64B.log. Первый телефон не объявляет ни одного codec,
|
||
Samsung объявляет только AVC. Нельзя считать такое состояние готовыми видеозвонками.
|
||
|
||
Дополнительно нужно разделить probe encode/decode: сейчас Android и desktop
|
||
консервативно объявляют decoder только после успешного собственного encoder.
|
||
Это лишает совместимого приёма устройства с неподходящим передатчиком.
|
||
Приёмный decoder следует проверять независимо эталонными потоками и общими limits.
|
||
Нужно проверить нормализацию HEVC tier по стандарту: High tier для level 3.1
|
||
нельзя просто разрешить как ещё один корректный режим.
|
||
|
||
Рекомендуемая доработка: H.264 Baseline (включая Constrained Baseline), HEVC Main,
|
||
отдельные проверенные encode/decode, DPB по уровню и фактическим SPS при reorder=0.
|
||
Сохранить проверку реального потока, ограниченные очереди и передачу bitrate на IDR;
|
||
не исправлять маркировку SPS без доказательства соответствия. Другой вариант —
|
||
поставлять на Android свои software codecs, сохранив прежний строгий контракт.
|
||
Дальнейшая реализация остановлена по правилу AGENTS.md о выявленной нестыковке.
|
||
После решения нужны проверки CPB/bitrate, камеры/смены Surface/lifecycle, полный
|
||
check.sh, Kotlin unit-тесты и реальные звонки Android↔Android↔ChatGUI.
|
||
|
||
Устранение аппаратных блокеров и проверка совместимости, 14.10.2026
|
||
Реализован согласованный план: Baseline/CB как отдельные AVC profiles, общий C-анализатор
|
||
VPS/SPS/PPS/HRD/IDR, нормативный DPB, независимые encode/decode probes с эталонными
|
||
потоками. Убрано неподтверждённое обещание VBV MediaCodec. Фактические profile/level
|
||
проверяются против CAPS после подготовки encoder; смена bitrate подтверждается
|
||
только настоящим IDR. Android допускает один экземпляр encoder и восстанавливает
|
||
прежний режим при отказе. Встречные направления выбирают codec независимо.
|
||
VIDEO_DECODE_OK/REJECT передают результат decoder; Auto исключает отвергнутый codec
|
||
для текущего выбора и пробует fallback. Старые generations не влияют на текущий.
|
||
Сетевой тест проверяет acceptance, rejection, stale result и сохранение запрета
|
||
отправки после очередного tick регулятора. При смене звонка очищаются pending команды port.
|
||
Сетевой долг ограничен полусекундой целевого потока плюс базовый RTT, с минимумом
|
||
64 KiB для IDR и абсолютным пределом 2 MiB. Частичная отправка по-прежнему ограничена
|
||
2с. На этом этапе предел отставания был 500мс; с 15.10.2026 заменён общим playout на 50мс (см. ниже).
|
||
|
||
Подтверждено на подключённых устройствах последней debug instrumentation:
|
||
- D2AR10P3600820: c2.unisoc.hevc.encoder Main/level 93/DPB=3; AVC Baseline/level 31.
|
||
Независимо работают c2.unisoc.hevc.decoder и AVC decoder (Baseline + CB).
|
||
- RZ8R72MG64B: c2.qti.avc.encoder CB/level 31. HEVC encoder отвергнут за tier=1,
|
||
level=93; независимо работает c2.qti.hevc.decoder и AVC decoder (Baseline + CB).
|
||
- На обоих телефонах последовательные открытия 4M -> 300k -> 4M прошли проверку
|
||
реального IDR/PTS; это проверка применения конфигурации, не измерение среднего CBR
|
||
на сложной сцене (синтетический серый кадр не требует целевой полосы).
|
||
- Фактический HEVC Unisoc декодирован Samsung; AVC Samsung декодирован Unisoc.
|
||
AVC Baseline Unisoc -> Samsung вернул 3/4 кадров: этот remote probe пока НЕ прошёл;
|
||
требуется диагностика задержанного выхода конечного потока. Каждый probe подаёт 4 IDR.
|
||
- Все три аппаратных потока декодированы production CallVideoDecoder ChatGUI.
|
||
Экспортные кадры синтетические, камера не включалась; /tmp/call-video-cross/.
|
||
|
||
Проверки: ./check.sh — 154/154, proxy 9/9, burst/load PASS (131с),
|
||
/tmp/call-profile-full-check.log. ChatGUI vibechat собран; codec QtTest 6/6 PASS,
|
||
/tmp/call-profile-codec-final.log. Перекрёстный production decode PASS,
|
||
/tmp/call-profile-cross-desktop.log. Android assembleDebug + 81/81 unit PASS,
|
||
/tmp/call-profile-cross-build.log; APK установлен на оба устройства.
|
||
Полная ручная приёмка звонка с реальными камерами/Surface, A/V и сменой режимов
|
||
остаётся отдельной проверкой; на рабочем Linux-хосте отсутствуют /dev/video*.
|
||
|
||
15.10.2026 — исправление desktop encoder при русской числовой локали
|
||
По логу звонка 23:58 USB-камера уже отдаёт кадры, но оба encoder probe отказали:
|
||
libx264 неверно разобрал level=3.1 и выдал level 30; libx265 отклонил level-idc=3.1.
|
||
Воспроизведено на production CallVideoEncoder: 1280x720/30, C — оба успешны,
|
||
ru_RU.UTF-8 — оба отказали. Параметры level теперь целочисленные (31 для уровня 3.1);
|
||
HEVC wire level 93 преобразуется делением на 3. Глобальная локаль приложения не меняется.
|
||
Строгая проверка фактического профиля/уровня сохранена; параметр encoder виден при video=debug.
|
||
Регрессия проверяет оба codec в C/ru_RU.UTF-8, декодирование 720p и смену 1M -> 300k -> 4M.
|
||
ChatGUI пересобран; test_call_video_encoder — 10/10 PASS в каждой локали, без skipped.
|
||
Логи: /tmp/call-camera-locale-test-c.log, /tmp/call-camera-locale-test-ru.log.
|
||
Работающий GUI автоматически не перезапускался; проверка полного звонка после исправления остаётся.
|
||
|
||
UI и периодические фризы (15.10.2026)
|
||
Android: принимаемое видео сначала занимает весь экран, своя камера — миниатюрой поверх него.
|
||
Назад возвращает к управлению звонком с двумя доступными миниатюрами (своей и собеседника).
|
||
Нажатие миниатюры открывает выбранное видео на весь экран; второе остаётся в углу и позволяет
|
||
поменять их местами. Выход в управление не выключает передачу/приём и не пересоздаёт TextureView/Surface.
|
||
Ориентация интерфейса фиксируется на время экрана звонка. Только большое видео дополнительно
|
||
поворачивается на 90 градусов, если так кадр занимает большую площадь; миниатюры не поворачиваются.
|
||
Весь кадр вписывается с исходными пропорциями, без crop и растяжения; при несовпадении пропорций остаются поля.
|
||
Выход с экрана звонка восстанавливает прежнюю политику ориентации Activity.
|
||
При видео экран остаётся включённым, proximity-overlay отключён. Если выбран EARPIECE,
|
||
переход на SPEAKER ждёт подтверждённого FAR; UNKNOWN/NEAR переключение запрещают.
|
||
Выключение видео, конец звонка и ручной выбор выхода отменяют ожидание. HEADSET не меняется,
|
||
автоследование за гарнитурой сохраняется. Отсутствующий датчик означает отсутствие proximity;
|
||
при отказе регистрации состояние остаётся UNKNOWN с предупреждением, автоматического переключения нет.
|
||
Без RX локальная камера сначала видна миниатюрой на экране аудиозвонка; без кадров
|
||
место под видео не резервируется. Касание показывает кнопки, через 5с они скрываются.
|
||
Имя/статус/диагностика не рисуются поверх видео; параметры вынесены в «Настройки звонка».
|
||
Кнопки и миниатюры учитывают вырез и systemBarsIgnoringVisibility, включая боковую панель
|
||
навигации и временно скрытые системные кнопки. Видео по-прежнему использует весь экран.
|
||
TextureView и Surface сохраняются при показе кнопок; decoder освобождает старый Surface
|
||
до его release. Запоздалый detach предыдущего Surface не отключает новый.
|
||
Preview получает прямой output той же Camera2 через общий StreamPreview/CameraPreviewView,
|
||
без Bitmap-конвертации и ограничения 5 fps. Surface удерживается до закрытия CameraCaptureSession/CameraDevice;
|
||
разворачивание/миниатюра меняют только геометрию TextureView. На SM подтверждено 29–30 fps.
|
||
Отказ preview output логируется и оставляет YUV-output передачи; encoder при attach/detach не перезапускается.
|
||
Desktop: первый RX кадр открывает отдельное изменяемое окно видео, без автоматического fullscreen.
|
||
Окно звонка с управлением и всеми настройками сохраняет свою геометрию и состояние настроек.
|
||
Видео сохраняет пропорции, своя миниатюра — в углу окна видео. F11/двойной щелчок переключают
|
||
fullscreen только для видео, Esc возвращает его обычную геометрию. Закрытие видеоокна
|
||
не завершает звонок; «Показать видео» открывает его повторно. Новые кадры сами не отменяют закрытие.
|
||
Preview берётся из того же QVideoFrame при появлении нового кадра, без прежнего ограничения 10 fps.
|
||
Только при отсутствии RX своя миниатюра
|
||
показывается в окне звонка; без кадров пустого видеоблока нет. После 5с без показанных кадров RX скрывается:
|
||
отдельного VIDEO_OFF в протоколе пока нет.
|
||
Desktop UI smoke: отдельные окна, сохранение геометрии/настроек звонка, resize видео,
|
||
F11/Esc, закрытие/повторное открытие без hangup, local-only и завершение — PASS
|
||
(/tmp/call-video-separate-preview.log). ChatGUI собран; Android assembleDebug и 81 unit-тест — PASS.
|
||
Автоматическое ожидание FAR и независимый поворот видео требуют проверки в следующем реальном звонке.
|
||
|
||
15.10.2026, диагностика низкого FPS desktop -> Samsung:
|
||
До исправления capture=30/s, encoded=20/s, paced=10/s; TX accepted=5/s, audio_busy=15/s,
|
||
audio_ahead=140–180ms. RX pulled/decoded/rendered=5/s, late=0: кадры терялись до отправки.
|
||
Вместо порога 33333мкс от прошлого кадра desktop использует бюджет 30 fps с запасом одного кадра
|
||
и минимальным интервалом 25мс. Бюджет сохраняет дробную часть при пропуске кадра; фиксированная сетка
|
||
теряла серии кадров при уходе фазы камеры. Тест охватывает 10 минут с дрожанием и квантованием PTS,
|
||
а также ограничение входных 40/60 fps. FIFO TX сохраняет кадры во время ожидания аудио.
|
||
test_call проверяет семь последовательных KEY/P кадров за audio barrier, порядок после release и предел PTS.
|
||
Диагностика: /tmp/call-fps-desktop-live.log, /tmp/call-fps-sm-live.log; unit/core/Qt проверки прошли.
|
||
|
||
Повторный звонок после FIFO: desktop offered/accepted около 30 fps, audio_busy=0;
|
||
Samsung pulled/decoded/rendered около 30 fps, late=0. Исчезновение обратного видео вызвано
|
||
тайм-аутом команды bitrate на SM: 03:07:05.899 timeout request=1, 03:07:05.992 запоздалый applied.
|
||
Автоматические IDR аппаратного кодека постоянно переносили программную границу следующего KEY.
|
||
Теперь период KEY отсчитывается от успешной подачи нашего запроса, а не от выходного IDR;
|
||
смена bitrate остаётся на границе KEY. Логи: /tmp/call-fps-sm-after.log, /tmp/call-fps-desktop-later.log.
|
||
|
||
Следующий живой звонок: RX desktop сохраняется несколько минут, около 30 fps. Остаточные 16–17 fps
|
||
desktop -> SM уже возникали в самой USB-камере: V4L2 interval=1/30, Exposure Dynamic Framerate=1.
|
||
Отключение этого флага во время звонка сразу дало capture/encode/accepted=30 fps, paced=0, audio_busy=0
|
||
(/tmp/call-video-fixed-camera-rate.log). На Linux звонок теперь отключает этот флаг после активации
|
||
QCamera и восстанавливает его после остановки; автоэкспозиция не меняется. Неподдерживаемый control
|
||
не мешает работе камеры. Лог video local preview отдельно показывает частоту и время обновления миниатюры.
|
||
Общий check.sh: 154/154, proxy 9/9, burst/load PASS; Qt codec/clock и Android 81 unit PASS.
|
||
|
||
Диагностика задержки desktop -> SM: локальный preview быстрый, но capture_age_us=-180000..-216000,
|
||
TX queue=6–8, queue_age_us=184000..248000, tail_ahead_us около 200000.
|
||
Первый запоздавший callback камеры привязывал относительный PTS к слишком позднему now;
|
||
следующие кадры оказывались в будущем и корректно ожидали audio barrier.
|
||
CallVideoCaptureClock теперь ограничивает mapping временем прихода каждого кадра и корректирует
|
||
offset только назад; поздний callback не увеличивает offset. Регрессия исходного PTS начинает новую привязку.
|
||
Тест моделирует 220мс задержки первого callback, её постепенное исчезновение, поздний callback и перезапуск PTS.
|
||
capture_age_us/clock_correction_us показывают привязку; queue_age_us использует только BOOTTIME,
|
||
tail_ahead_us сравнивает media PTS в MONOTONIC. Разность BOOTTIME и MONOTONIC не является задержкой.
|
||
Qt encoder/clock PASS; изменение требует повторного живого звонка после перезапуска ChatGUI.
|
||
Лог исходного дефекта: /tmp/call-latency-new-sample.log.
|
||
|
||
Подтверждённая причина фризов раз в 1–2с: коррекция capture timestamp меняла audio source_epoch,
|
||
а обе платформы сбрасывали video decoder и ждали KEY. Теперь source_epoch обновляет
|
||
аудиопривязку без потери video references, готовых кадров и последнего показанного PTS.
|
||
Отдельный production RX тест: 90/90 кадров, 32 смены аудиоэпохи, 0 дополнительных KEY,
|
||
0 decoder rejection (/tmp/call-video-clock-final.log).
|
||
Android больше не отбрасывает кадр из-за интервала чуть меньше 33333мкс: Camera2 уже
|
||
ограничивает FPS; повторная фильтрация превращала дрожание timestamps в падение до 15fps.
|
||
Desktop rate test: 10/10 PASS, непрерывные H.264/H.265 4M -> 300k -> 4M, оба decoder.
|
||
H.264 commit 5–7мс; H.265 51–76мс включая кодирование первого кадра, подготовка — в фоне.
|
||
Измерение /tmp/call-video-background-test.log; для H.265 стоимость кодирования остаётся существенной.
|
||
Android assembleDebug и 81 unit-тест проходят. Полный звонок с камерами после этих правок
|
||
требует ручной приёмки; автоматические проверки не подтверждают его целиком.
|
||
|
||
Аппаратная смена bitrate без перезапуска подтверждена на шумовом YUV с ограниченным контрастом:
|
||
Samsung AVC: 3.985M -> 306.8k -> 3.990M; Unisoc HEVC: 4.031M -> 291.9k -> 4.042M;
|
||
Unisoc AVC: 3.993M -> 29.3k -> 3.994M бит/с. В каждой пробе получены все 360 кадров
|
||
с точными PTS и проверенными KEY. При сильной квантизации шум пропадает; фактический
|
||
выход ниже target допустим. Проверяются верхняя граница low-rate и восстановление high-rate,
|
||
а не наличие filler до target. Полностью случайный 8-bit шум в первой пробе Samsung
|
||
превысил 4M: аппаратный CBR не является жёстким ограничителем выходного потока.
|
||
Сетевой admission/ограничитель очереди остаётся необходимым.
|
||
Логи: /tmp/call-video-dynamic-sm-detail2.log, /tmp/call-video-dynamic-sp-final.log,
|
||
/tmp/call-video-sp-rates-final.log. CallVideoProbe принимает -e codec 1/2 для отдельной пробы.
|
||
Итоговый APK установлен на оба телефона. check.sh: 154/154, proxy 9/9, burst/load PASS (119с),
|
||
/tmp/call-video-core-final-check-unsandboxed.log. Первый запуск в sandbox не мог открыть сокеты;
|
||
успешный полный прогон выполнен с разрешённым сетевым доступом.
|
||
|
||
15.10.2026, повторная диагностика задержки desktop -> Samsung после внедрения AAudio
|
||
Звонок 14610053360084326214 (Kotlin Long: -3836690713625225402), Samsung на громкой связи,
|
||
AAudio output ~23мс, очередь устройства 20мс, стабильный JB reserve=20мс. Оба направления около 30fps.
|
||
Выборка всех этапов по pts % 1000000 < 34000: примерно один кадр/сек, категории debug=debug.
|
||
Desktop: encode (включая select/convert), send, receive, decode, select, publish.
|
||
Android: receive, pull (MONOTONIC+BOOTTIME), queue, decode, release, surface, texture_update, commit.
|
||
Поля pts принадлежат отправителю; mono_us — локальному CLOCK_MONOTONIC, boot_us — CLOCK_BOOTTIME.
|
||
Сравнение разных устройств напрямую запрещено. Для анализа использовано ограничение send <= receive
|
||
в обоих направлениях, по совпадающим PTS: phone_boot-host_boot в [-796330256933, -796330252177]мкс.
|
||
Погрешность межмашинных оценок в этой выборке +/-2.378мс; локальные интервалы этой ошибки не имеют.
|
||
|
||
Первый замер ошибочно считал texture_update временем готовности текущего кадра: по исходникам Android
|
||
TextureView.applyUpdate() передаёт работу RenderThread, а callback вызывается до фактического dequeue.
|
||
Чтение SurfaceTexture.timestamp здесь часто показывает предыдущий кадр и добавляет ложные ~33мс.
|
||
MediaCodec.OnFrameRenderedListener на этом backend возвращает исходный PTS в nanoTime, что также
|
||
не является измерением локального времени показа. В логе поле переименовано в reported_us.
|
||
Контроль commit читает timestamp после registerFrameCommitCallback (API29+): кадр уже обработан
|
||
RenderThread и передан в swapchain. Callback может опоздать; физический scanout не измерен.
|
||
Эти данные не обосновывают компенсацию постоянными 40мс или замену TextureView.
|
||
|
||
51 полная выборка desktop -> Samsung: медиана encode 25.72мс, от mapped capture PTS до выхода
|
||
encoder 46.19мс, сеть 4.09мс, ожидание в RX-порту 32.05мс, decode 6.47мс,
|
||
ожидание audio clock после decode 24.19мс, release -> commit 19.50мс.
|
||
Медиана mapped PTS -> release 115.88мс, -> commit 137.90мс.
|
||
В последних 20 выборках без большого сетевого выброса: PTS -> release 100.4мс,
|
||
PTS -> commit 133.8мс; сеть 4.4мс, decode 5.7мс. Медианы отдельных этапов не складываются
|
||
в точную медиану полного пути. В общей записи были сетевые выбросы и увеличение ожидания JB;
|
||
на стабильном участке резерв вернулся к 20мс, аппаратных underrun нет.
|
||
|
||
Обратный путь, последние 20 выборок: sensor PTS -> send 124.7мс (камера и encoder вместе),
|
||
сеть 5.2мс, software decode 2.1мс, ожидание audio clock <0.1мс, select -> Qt publish 12.0мс.
|
||
От sensor PTS до publish 147.5мс; publish ещё не означает физический показ на мониторе.
|
||
Desktop capture PTS привязан к arrival-upper-bound Qt, Android PTS — к аппаратному sensor timestamp:
|
||
сравнивать эти числа как точное glass-to-glass измерение разных камер нельзя.
|
||
Тем не менее асимметрия после получения кадра ясна: быстрый desktop encoder обгоняет аудиопозицию,
|
||
Samsung удерживает кадр/следующие encoded frames до неё. Видео телефона приходит к desktop уже позже
|
||
соответствующей аудиопозиции и показывается практически сразу. Локальный desktop preview аудио не ждёт.
|
||
Ускорение только encoder освободит CPU, но при раннем приходе увеличит ожидание аудио на столько же.
|
||
Для дальнейшего снижения общей A/V-задержки нужно оптимизировать аудиотракт и отдельно проверять
|
||
планирование презентации с учётом измеренного render latency; JB20/адаптация и привязка к звуку сохранены.
|
||
|
||
Снимки: /tmp/video-timing-committed-desktop.log, /tmp/video-timing-committed-sm.log;
|
||
расчёт: /tmp/video-timing-committed-analysis.json. Исходники Android для проверки порядка callbacks:
|
||
/tmp/video-TextureView.java, /tmp/video-DeferredLayerUpdater.cpp, /tmp/video-ImageConsumer.cpp.
|
||
Сборки /tmp/video-timing-*-build.log и /tmp/video-commit-*-build.log; 103 Android unit PASS,
|
||
test_call_video_clock exit=0, test_call_video_encoder 13/13 PASS. Диагностика установлена на Samsung,
|
||
ChatGUI пересобран и перезапущен. Поведение кодеков, очередей, аудиосинхронизации не изменено.
|
||
|
||
Независимое расписание показа, 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;
|
||
это дополнительное локальное поле, wire format не изменён. Desktop использует тот же источник
|
||
времени, Android System.nanoTime соответствует MONOTONIC. BOOTTIME из транспортных логов сюда не подмешивается.
|
||
Если received_us > D[i], срок переносится на received_us. Поздний pull, decode или poll не переносят
|
||
его на фактическое время показа. Поэтому ожидание данных постепенно съедается ускорением,
|
||
а опоздание CPU не добавляется к последующим срокам. На полностью разгруженном входе скорость
|
||
естественно ограничена поступлением кадров; дополнительных кадров и фиксированного запаса не создаём.
|
||
|
||
Свежий audio clock ограничивает фазу: ранний кадр ждёт, поздний более 50мс не показывается.
|
||
Проекция аудиометки на 1x ограничивает плановый срок, но решение SHOW всегда проверяет текущие часы;
|
||
проверка не реже 5мс, поэтому изменение темпа аудио не требует отдельного rate estimator.
|
||
При WAIT кадр не потребляется, следующая проверка не накапливает сдвиги. При длинной паузе CPU
|
||
готовый следующий кадр с уже наступившим сроком вытесняет устаревший показ; codec references не удаляются.
|
||
После невалидных аудиочасов снимается привязка расписания, но сохраняются decoder и монотонный порядок PTS.
|
||
Изменение source_epoch само по себе не сбрасывает видео. Долг таймера после outage не отрабатывается пачкой.
|
||
|
||
Обе платформы держат до двух готовых изображений для lookahead; ждать второго для показа первого не нужно.
|
||
Android продолжает подачу input во время ожидания output, суммарно до восьми кадров в decoder/готовой очереди.
|
||
Метка received_us переносится через input FIFO по PTS. EOS закрывает decoder лишь после показа/пропуска
|
||
всех выходов, включая непустой последний EOS output. Переход формата сохраняет расписание.
|
||
На desktop публикация remote image больше не зависит от UI-таймера 20мс: одно coalesced queued notification,
|
||
последний выбранный кадр и проверка поколения публикации при stop/restart. Preview/status сохраняют свой таймер.
|
||
|
||
Сводка call/debug раз в секунду: shown, drop_late/drop_schedule/drop_order, cadence_us,
|
||
av_us, dispatch_max_us, data_shift_us, audio_wait/clock_wait. Для самого большого промежутка
|
||
gap_pts_us/gap_rx_us/gap_dispatch_us различают нерегулярность исходных кадров, доступности входа и исполнения.
|
||
data_shift_us — сумма сдвигов расписания по доступности входа, не накопленная задержка сети:
|
||
даже ровный вход 30fps ограничивает 1.04x примерно на 40мс за секунду.
|
||
Задержку uasync/его backpressure эта метка не отличает от задержки доставки; это не физический socket timestamp.
|
||
Граница A/V относится к выдаче на отображение. UI/render/scanout измеряются отдельно, строгой аппаратной
|
||
гарантии в условиях остановки CPU или отсутствия новых кадров нет. Для большой сетевой паузы одновременно
|
||
сохранить все кадры, только плавно догнать и сразу соблюдать 50мс невозможно — действует drop_late.
|
||
|
||
Проверки: test_call_video_playout — абсолютные сроки при CPU+9мс, задержка данных+20мс,
|
||
пропуск backlog, верхняя граница A/V, outage/recovery, порядок PTS и 400 кадров с jitter/FPS/темпом аудио.
|
||
Комбинированный сценарий: 389 показано, 11 пропущено, все SHOW внутри A/V-границ.
|
||
Qt test_call_video_clock использует настоящий decoder: ровный вход 90/90; доставка+20мс,
|
||
RX CPU+80мс и UI+60мс — 87/90, без key requests/rejections и без публикаций после stop.
|
||
Повторный start того же call_id и 32 source_epoch за сценарий не разрывают декодирование.
|
||
test_call_video, test_call_video_rate, test_call (2/2), Qt encoder (13/13), Android unit (103) прошли.
|
||
Ядро, ChatGUI, APK собраны. Логи /tmp/video-playout-*-test.log и /tmp/video-playout-*-build.log.
|
||
|
||
Пробный живой звонок 11342339676311998023: Samsung 3719 показов за 127.374с (~29.2fps),
|
||
drop_late/drop_schedule=0, av=-43819..39982мкс. Desktop 5737 показов за 217.560с (~26.4fps),
|
||
646 drop_late, 3 drop_schedule, av=-49934..39988мкс. Окна выборок различаются, это не парный glass-to-glass замер.
|
||
На Samsung остаются отдельные промежутки 50–70мс; на desktop обратное направление теряет около 10%
|
||
кадров по строгой границе отставания. Полностью ровный живой показ пока не подтверждён.
|
||
Снимки /tmp/video-playout-live-{desktop,sm}.log. После этого замера добавлен контекст максимального
|
||
промежутка и уточнён абсолютный срок сна Android; финальная сборка установлена на Samsung и Q8.
|
||
Следующая проверка: по gap_* установить источник остаточных пауз и late drops, не расширяя A/V-границы вслепую.
|
||
|
||
Проверка финальной сборки, звонок 9002196988226353494, 15.10.2026
|
||
Снимки /tmp/video-playout-final-live-{desktop,sm}.log, сводка /tmp/video-playout-final-live-summary.json.
|
||
Последние 20 секунд: Samsung 29.94fps, ноль drop_late/drop_schedule; videoPTS-audioPTS на release
|
||
+14.664..+40.000мс. Медиана секундных максимумов интервала 43.14мс; отдельные длинные интервалы остаются.
|
||
Desktop 29.61fps, 8 drop_late за это окно, без drop_schedule; A/V -49.740..+2.763мс.
|
||
Исходный период Android-камеры около 33.308мс, но для самых длинных промежутков на desktop
|
||
медианный gap_rx=52.91мс при gap_dispatch=3.95мс. Нерегулярность видна уже на входе RX port.
|
||
На Samsung: gap_pts обычно ~36мс, gap_rx медиана 56.34мс, gap_dispatch 7.06мс;
|
||
был исходный промежуток 68мс. Поэтому все остаточные паузы нельзя приписать приёмному таймеру.
|
||
|
||
По согласованным send/receive PTS двух направлений phone_boot-host_boot ограничен
|
||
[-796330268513,-796330265026]мкс: погрешность оценки +/-1.744мс.
|
||
После завершения постановки кадра отправителем до полной сборки получателем: медианы
|
||
3.03мс desktop->SM (42 выборки) и 3.37мс SM->desktop (33). Это не включает queue_age отправителя.
|
||
На SM sensorPTS->send медиана 124.08мс, максимум 163.26мс. Для desktop mappedPTS->send
|
||
медиана 30.28мс, encode 23.11мс. Разные способы получения capture PTS не дают точного
|
||
сравнения стекло-стекло. На SM receive->decode 11.78мс, decode->release 20.68мс,
|
||
release->commit 23.29мс по имеющимся совпавшим выборкам; commit не означает физический scanout.
|
||
|
||
Выявлен отдельный механизм искусственного ожидания TX: flush выдаёт до 16 фрагментов по 1200 байт,
|
||
затем продолжение вызывается лишь новым видео, отправкой audio или таймером 50мс.
|
||
Это граница 19200 байт на один вызов, хотя канал может быть свободен. В выборке Samsung:
|
||
31 кадр <=19200 байт имел queue_age 0..0.2мс; кадры 45309/46333 байт — 1.9/39.9мс.
|
||
Снимки непустой TX-очереди показывают held=0, audio_busy=transport_busy=0, то есть ожидания
|
||
аудиопозиции в этих снимках нет. Покадрового счётчика выхода по budget/debt сейчас нет;
|
||
39.9мс нельзя целиком объявить только budget, но само ожидание внешнего события после budget
|
||
подтверждено кодом. Следующее исправление: ограниченное отменяемое продолжение отправки при
|
||
исчерпании budget и готовых данных, с сохранением проверок аудиопозиции, сетевого долга и fairness uasync.
|
||
Не увеличивать очереди/допуск A/V для маскировки этой паузы. В этом диагностическом проходе код не менялся.
|
||
|
||
Продолжение TX после исчерпания budget, 15.10.2026
|
||
После успешной порции из 16 фрагментов flush ставит один uasync_call_soon, если остался кадр,
|
||
разрешённый audio_end_pts и подтверждёнными CAPS. Новый callback выдаёт следующую ограниченную порцию.
|
||
uasync обрабатывает только исходный batch очереди immediate, поэтому самопостановка не захватывает
|
||
цикл целиком; между batch остаётся обработка сокетов/таймеров. Новый поток или периодический таймер не нужен.
|
||
Каждый фрагмент по-прежнему проверяет capabilities, deadline, media debt и место в router send_q.
|
||
Выход по backpressure не ставит новое immediate-продолжение; возобновление остаётся за штатными событиями/tick.
|
||
Другой flush (например, новый audio block) отменяет прежнее ожидающее продолжение. Callback перед работой
|
||
снимает собственный handle; stop/discard отменяют pending до освобождения v/данных. Все операции в uasync-потоке.
|
||
|
||
Без новых покадровых логов: секундный video TX flow содержит flush_yields (постановки), flush_runs
|
||
(выполненные callback), flush_wait_max_us (наибольшее ожидание продолжения, включая опережающий audio flush),
|
||
flush_net_wait (выходы по debt/send_q), flush_audio_wait (выходы по audio/CAPS-барьеру).
|
||
Выборочный stage=send дополнен budget_yields текущего кадра; queue_age_us сохраняет полный срок в TX.
|
||
Эти счётчики различают исчерпание порции, задержку исполнения и ограничение транспорта; они не являются
|
||
отдельным измерением физической сети и не должны складываться с queue_age.
|
||
|
||
test_call_video_flush использует настоящий transport, сериализацию фрагментов и uasync, управляемый router.
|
||
45001 байт отправляются тремя порциями (16/16/6), без audio/50мс tick, с проверкой каждого байта и offset.
|
||
Проверены остановка при отсутствии места/большом inflight, возобновление без дублей, audio/CAPS-барьеры,
|
||
опережающий audio flush, stop/discard с pending callback, равенство alloc/free immediate handles.
|
||
Тест прошёл; test_call_video/rate/playout, сетевой test_call (2/2), Qt encoder (13/13), Qt clock
|
||
(90/90 ровный вход, 87/90 с задержками, ноль key requests/rejections) также прошли.
|
||
Ядро, ChatGUI, APK собраны; Gradle unit-задача актуальна, 103 теста без ошибок (up-to-date).
|
||
Артефакты проверки: /tmp/video-flush-*-build.log, /tmp/video-flush-*-test.log,
|
||
/tmp/video-flush-regression.log. APK установлен на Samsung и Q8, ChatGUI перезапущен.
|
||
Непрерывные журналы пишутся; эффект на живой queue_age/равномерность ещё требует повторного звонка.
|
||
|
||
Живая проверка продолжения TX, звонок 12694689407894286842
|
||
Снимки /tmp/video-flush-live-{desktop,sm}.log; /tmp/video-flush-live-summary.json и -stages.json.
|
||
На Samsung два выборочных крупных кадра 42429/43309 байт: budget_yields=2, queue_age=600/500мкс.
|
||
В предыдущем звонке кадр 46333 байт ждал 39900мкс. Это разные кадры/звонки, но текущие выборки
|
||
и счётчики подтверждают работу немедленного продолжения. Desktop крупных кадров в выборочной трассе
|
||
не было; секундные счётчики показывают 26 постановок/26 callback за последние 20 окон.
|
||
На SM 83 постановки/78 callback: оставшиеся продолжения опережены обычными вызовами flush.
|
||
flush_net_wait=0, непустых TX-очередей в этих снимках нет. Максимальное ожидание продолжения:
|
||
desktop 201мкс, SM 5877мкс. Ожидание исполнения не считается задержкой сети.
|
||
|
||
Последние 20 отчётов playout: SM 599 показов/20.052998с=29.87fps, ноль drops,
|
||
videoPTS-audioPTS -1287..39997мкс; промежутки показа 6931..63810мкс.
|
||
Desktop 600/20.057555с=29.91fps, 3 late drops, ноль schedule drops, A/V -49290..1469мкс;
|
||
промежутки 22672..83153мкс. Полностью ровного cadence ещё нет.
|
||
Медиана gap_rx для самого длинного секундного промежутка: SM 51560.5мкс, desktop 56718.5мкс;
|
||
соответствующая медиана gap_pts 32046/33308мкс. Максимальный dispatch SM 24404мкс, desktop 9044мкс.
|
||
|
||
Парные выборки только этого call_id дают phone_boot-host_boot [-796330272225,-796330268949]мкс
|
||
(+/-1638мкс). Send->complete receive: медиана desktop->SM 3469мкс (47), SM->desktop 3948мкс (46).
|
||
В последних 20сек выборочного SM timing: receive->decode 11661мкс, queue->decode 7424.5мкс,
|
||
decode->release 4199.5мкс; release->commit 23007мкс (15 совпавших выборок).
|
||
Медианы не складываются в точный end-to-end; commit по-прежнему не является физическим scanout.
|
||
|
||
Есть краткие переходы audio clock valid=0->1. В последних 20сек именно clock-трассы 37 пар,
|
||
медиана длительности 2173мкс, максимум 6540мкс. Это не количество потерянных кадров:
|
||
poll работает и без готового изображения. При invalid общий playout сбрасывает anchored.
|
||
CallTimeline::clock имеет несколько причин отказа (в том числе nowUs<anchorReceived), но логирует
|
||
только итоговый valid. nowUs передаётся в call_audio_get_clock до захвата mutex; это потенциальная
|
||
гонка времени снимка с progress, ещё не доказанная причина текущих переходов.
|
||
Следующий шаг: агрегировать причины invalid и длительность получения снимка/возраст anchor,
|
||
с контекстом максимального gap; не ослаблять valid и не менять часы без подтверждающих измерений.
|
||
|
||
GPU-кодирование desktop-звонка, 18.10.2026
|
||
В основном пути CallVideoEncoder выбирает только hardware: NVENC, QSV, AMF, VAAPI.
|
||
Software доступен только при явно указанном backend в проверках; автоматического CPU fallback нет.
|
||
Общий LiveVideoEncoder задаёт минимальный настроенный конвейер, без B-кадров/lookahead,
|
||
проверяет профиль/level/первый IDR. QSV работает внутри в 90kHz, capture PTS восстанавливается
|
||
по точному соответствию codec PTS; порядок выходов проверяется. До восьми входов, без неограниченной очереди.
|
||
NVENC использует delay=0, QSV/VAAPI async_depth=1, AMF ultralowlatency и async_depth=1 при наличии опции.
|
||
В старом FFmpeg AMF не поддерживает forced_idr; общий путь сохраняет периодические IDR,
|
||
как в трансляции экрана. Это видно в forced_keys/forced_idr и не выдаётся за поддержку принудительного IDR.
|
||
|
||
Worker сразу отдаёт все готовые кадры. Видеопорт атомарно копирует готовую пачку до восьми кадров
|
||
с общим пределом 2MiB в один mailbox и ставит один post; ожидания накопления пачки нет.
|
||
Busy возвращается до принятия любого кадра. После отказа транспорта зависимый хвост освобождается,
|
||
запрашивается KEY. Stop/detach освобождают всю ожидающую пачку. Android передаёт count=1.
|
||
При смене bitrate новый GPU готовится в фоне; до его первого проверенного IDR продолжают выходить
|
||
старые кадры с PTS раньше границы замены. Applied выдаётся после IDR, при отказе сохраняется старый GPU.
|
||
|
||
Диагностика: debug=trace,video=trace,call=debug. call GPU probe показывает pipeline_frames,
|
||
video encoder flow — pending, bitrate committed — pipeline_frames/pipeline_pts_us/retire_us.
|
||
test_call_video_gpu проверяет шесть Windows backend/codec сочетаний на настоящих bitstream и decoder,
|
||
с подменой GPU-границы: задержка, пачка из трёх кадров, точные PTS и отказ замены; это не тест драйвера.
|
||
Сетевой test_call проверяет атомарную пачку, busy, неверную пачку и teardown.
|
||
Фактическую глубину, время GPU и задержку показа необходимо подтвердить живым звонком на Windows.
|
||
|
||
AMF HEVC PPS, исправление 18.10.2026
|
||
В Windows-логе GPU выдавал IDR, но call verifier отвергал разрешённые PPS-флаги
|
||
dependent_slice_segments_enabled_flag/output_flag_present_flag ещё до отправки. encode оставался нулевым.
|
||
Общий verifier поддерживает эти флаги: первый IDR-слайс независим, последующие segments
|
||
проверяются с учётом CTB геометрии SPS и optional dependent flag; явный pic_output_flag должен быть 1.
|
||
Обе стартовые KEY-пробы проходят тот же call_video_check_key, что и live-путь, затем FFmpeg decoder.
|
||
Несовместимый backend не попадает в capabilities; выбор продолжает перебор GPU, без CPU fallback.
|
||
test_call_video проверяет сочетания PPS flags/extra header bits, independent/dependent segments,
|
||
отказ при выходе адреса за CTB и при отключённом picture output. test_call_video_gpu меняет PPS
|
||
на настоящем HEVC IDR и проверяет полный pipeline с decoder, а также отказ несовместимой стартовой пробы.
|
||
Общий verifier используется и на Android: для HEVC обновить обе стороны звонка. Буферизация не меняется.
|
||
|
||
Аудит восстановления после коротких пауз, 19.10.2026
|
||
Новое требование: сохранять последовательность encoded кадров, быстро декодировать пришедший
|
||
burst и плавно догонять показом. Короткая пауза не должна вызывать пропуск входного P-кадра,
|
||
ожидание следующего KEY или перескок на более свежее изображение.
|
||
На момент аудита реализация этому требованию не соответствовала; сначала добавлена диагностика,
|
||
алгоритм буферизации/показа и действующие ограничения не изменены.
|
||
|
||
1. Router сохраняет порядок, при заполнении RX port приостанавливает доставку/ACK и возобновляет
|
||
её после pull. RX port имеет 8 кадров/4MiB; это предел ёмкости, не обязательный стартовый запас.
|
||
2. Desktop декодирует последовательно, держит до двух готовых изображений. Android ограничивает
|
||
decoder inputs + готовые outputs восемью кадрами, готовые outputs — двумя. Подача/декодирование
|
||
в итоге ограничиваются освобождением готовых кадров, отдельного быстрого drain всего burst нет.
|
||
3. Playout имеет номинальное расписание 1.04x. При срыве поставки срок двигается по received_us,
|
||
но позднее выполнение worker само по себе срок не переносит. DROP_LATE отбрасывает показ при
|
||
отставании от audio PTS больше 50мс; DROP_SCHEDULE пропускает изображение, если следующий
|
||
кадр тоже уже пора показать. Оба пути работают после decode, codec references сохраняются.
|
||
4. Desktop GUI хранит последнее выбранное изображение вместо FIFO. Пока queued notification
|
||
не исполнено, новые publishImage заменяют предыдущее. Это ещё один пропуск показа после
|
||
decode. Прежний ui_wait_us считался от последней замены, поэтому мог скрыть паузу GUI;
|
||
диагностика теперь отдельно считает ожидание notification, возраст последней замены и overwritten.
|
||
5. Потеря до decoder тоже возможна. TX mailbox освобождает всю пачку после попытки передачи,
|
||
включая кадр с результатом busy=0 и оставшийся хвост. Producer получает запрос KEY, а не retry.
|
||
Admission закрывается при receive_busy или достижении предела долга; transport также имеет
|
||
предел 32 кадров/500мс PTS/2MiB и deadline головы 2с. Отказ готовому encoded кадру разрывает
|
||
цепочку references; последующие P-кадры ждут KEY. Это отличается от DROP_LATE/SCHEDULE.
|
||
|
||
Контрольная симуляция настоящего call_video_playout (90 кадров, 30fps, пауза 200мс):
|
||
пауза доставки при непрерывном аудио — показано 86, late=4;
|
||
пауза CPU при непрерывном аудио — показано 84, late=5, schedule=1;
|
||
пауза CPU и удержание audio PTS с последующим догоном 1.1x — показано 77, late=12, schedule=1.
|
||
Шаг poll в этих сценариях 1мс; при шаге 15.625мс последний сценарий даёт 74 показа и 15 late.
|
||
Шаг 15.625мс — параметр модели, не утверждение о фактическом Windows timer resolution.
|
||
Даже при идеальном исполнении 1.04x убирает дополнительные 200мс примерно за 5с.
|
||
Существующий test_backlog явно требует DROP_LATE и DROP_SCHEDULE после паузы 102мс;
|
||
его успешность подтверждает старую политику, а не новое требование сохранения кадров.
|
||
|
||
Для нового поведения нужны согласованные изменения:
|
||
- encoded FIFO с переносом backpressure до producer и сохранением неотправленного хвоста;
|
||
- последовательная подача decoder без ожидания срока показа, пока хватает выходной ёмкости;
|
||
- упорядоченный показ с адаптивным ускорением по накопленной длительности, без late/schedule
|
||
пропусков при короткой паузе. Возобновление от первого сохранённого кадра, без мгновенного burst;
|
||
- сохранение выбранного кадра до фактического потребления GUI/render, вместо overwrite последним;
|
||
- временное A/V-отставание во время восстановления либо согласованная задержка аудио и видео.
|
||
Сохранить все кадры после 200мс паузы и одновременно требовать A/V <=50мс невозможно.
|
||
Предел короткой паузы, память и политика длительного переполнения должны задаваться явно.
|
||
Обычный догон не должен запрашивать KEY или сбрасывать decoder.
|
||
|
||
Windows-диагностика: AudioDiag теперь работает через тот же bounded reporter, что и Linux.
|
||
При call=debug WASAPI queue показывает padding и stream latency отдельно, callback/write —
|
||
время исполнения/интервалы; 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.
|
||
В актуальной схеме ниже три decoded места, включая ожидающий подтверждения GUI/Surface кадр;
|
||
входы decoder ограничены отдельно восемью, чтобы output lookahead не блокировал запуск GPU.
|
||
Основной backlog хранится encoded, а не большим RGB FIFO или удержанием всех аппаратных outputs.
|
||
|
||
Playout больше не содержит DROP_LATE/DROP_SCHEDULE. Invalid/order остаётся диагностируемой ошибкой.
|
||
CallTimeline публикует rate_ppm из фактического SourceSpan SoundTouch -> PCM на текущей позиции устройства.
|
||
Первоначальный вариант управлял темпом по audioPTS-framePTS. Его заменила описанная ниже схема
|
||
с подтверждённым временем показа, общей глубиной конвейера и небольшим рабочим запасом.
|
||
Видео может временно отставать от звука более 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 драйвера.
|
||
|
||
Windows 1.log, повторный прогон 19.10.2026
|
||
В 19:16:31–19:16:41 decoder занимает 1.7–4.4мс, но notification ждёт до 113мс,
|
||
а подтверждение paint — до 227мс. Показ падает до 3–6fps, RX FIFO заполняется,
|
||
ожидание уже принятого кадра до decode достигает 2.66с. Аудио в этот период работает:
|
||
WASAPI padding 20мс, callback около 10мс, аудиочасы valid=1. Это локальное ограничение GUI;
|
||
длительность самого paint и причина задержки event loop в старой диагностике не различались.
|
||
После 19:16:41 notification обычно <1.8мс, paint acknowledgement <9мс, показ 29–32fps.
|
||
Остаточное опоздание RX sleep достигает 19.2мс; точность Windows timer отдельно не исправлялась.
|
||
Отсутствие кадров около 19:17:04–19:17:10 заканчивается generation=2; пользователь подтвердил один перезапуск.
|
||
|
||
Выявлена дополнительная искусственная пауза playout: gap_pts_us=2264933 при готовом lookahead
|
||
превращался в cadence_us=1424602 из-за deltaPTS/1.6. Захват мог быть приостановлен upstream
|
||
backpressure, но эта пауза проходилась второй раз на показе. Теперь интервал ограничен FPS текущего
|
||
формата, реальное опережение по-прежнему удерживается audio PTS. Encoded/decoded кадры не удаляются.
|
||
source_gaps/source_gap_max_us и первый video source gap за секунду показывают устранённый лишний интервал.
|
||
Тест queued_capture_gap моделирует готовые соседние кадры с разрывом 0.5/2.265/10с на 15/30/60fps;
|
||
следующий кадр выходит за один обычный интервал, а граница опережения аудио 40мс сохраняется.
|
||
|
||
Для установления причины стартовой задержки GUI добавлены call=debug сводки:
|
||
- video GUI loop: частота timer 20мс и максимальный разрыв event loop, первая задержка >50мс — WARN;
|
||
- video GUI delivery image_slot_max_us: время синхронных slots imageChanged, отдельно от notification wait;
|
||
- video GUI paint: ожидание UpdateRequest -> paintEvent, prepare/draw/overlay/painter.end,
|
||
размеры изображения/окна, DPR при первом замедлении, число move/resize событий.
|
||
Замена renderer или изменение Windows system timer без этих измерений не выполнялись.
|
||
|
||
Часы показа и сквозная очередь, 19.10.2026 (актуальная схема)
|
||
N = полные encoded в port + pending input worker + принятые decoder inputs без output
|
||
+ decoded FIFO + один выбранный, но ещё не подтверждённый GUI/Surface кадр.
|
||
Перемещение между стадиями не меняет N; исключение кадра происходит после подтверждения показа
|
||
или решения DROP_LATE для уже декодированного кадра (дополнение 20.10.2026 ниже).
|
||
Снимок port.rx_frames берётся под mutex после локальных перемещений RX worker. Сетевая частичная
|
||
сборка не считается полным кадром. Очереди отправителя не видны этому счётчику.
|
||
RX admission возобновляется при появлении места, без искусственного ожидания полного drain FIFO.
|
||
Уведомление port будит desktop RX немедленно; callback снимается до освобождения владельца.
|
||
|
||
Декодирование не следует FPS показа. Оно запускается при готовых данных и свободном decoded месте.
|
||
Три decoded места включают GUI/Surface pending; внутренний pipeline GPU ограничен восемью входами отдельно.
|
||
submit/receive независимы: EAGAIN сохраняет непринятый packet. Metadata PTS/frame_id/generation/received_us
|
||
переходит в выход ровно один раз. Перед новым format старый decoder завершается EOS и отдаёт все принятые входы.
|
||
Уже готовые изображения старого поколения остаются в FIFO до последовательного показа.
|
||
|
||
SHOW резервирует кадр, presented(PTS, MONOTONIC) подтверждает его потребление и продвигает часы.
|
||
Desktop ACK приходит после paintEvent; Android — после SurfaceTexture/RenderThread commit, не по OnFrameRendered.
|
||
Это подтверждения платформенного потребления, а не измерение физического scanout экрана.
|
||
Срок следующей головы фиксируется один раз: последнее ACK + период format / speed - оценка dispatch-to-ACK.
|
||
Оценка lead сглаживается и принимает обычный ACK за целый цикл экрана; выбросы больше 1.25 периода +1мс игнорируются.
|
||
Пустая очередь сохраняет привязку: готовый через 12мс следующий кадр не показывается раньше своего обычного срока.
|
||
Поздно пришедшая голова выходит сразу после готовности; разрыв capture PTS не повторяется ожиданием.
|
||
|
||
Целевой N: текущая голова + один будущий кадр + необходимый pipeline выбранного decoder.
|
||
Избыток каждого кадра добавляет 0.1x к базовому max(1x, audio rate), предел 1.6x.
|
||
Недостаток рабочего запаса временно уменьшает темп на 0.02x; обязательного старта с тремя кадрами нет.
|
||
Изменение темпа сглаживается с постоянной 200мс и применяется к следующему назначаемому сроку.
|
||
Слабая A/V-коррекция (до 0.05x) видит скрытый сетевой долг сверх рабочего запаса +100мс.
|
||
Опережение аудио по-прежнему ограничено 40мс; невалидные аудиочасы запрещают новый показ без очистки FIFO.
|
||
|
||
Windows ожидает абсолютный срок через high-resolution waitable timer + event, без spin/timeBeginPeriod.
|
||
Отказ high-resolution API явно логируется, остаётся regular timer; полный отказ handles использует condition_variable.
|
||
Stop и данные прерывают ожидание. Фактический overshoot остаётся в video RX worker wake_late_max_us.
|
||
|
||
CPU encoder: libx264 threads=4/sliced-threads=1; libx265 pools=4/wpp=1/frame-threads=1.
|
||
Это четыре вычислительных worker, не обещание ровно четырёх OS threads вместе со служебными.
|
||
GPU encoder сохраняет приоритет NVENC/QSV/AMF/VAAPI, после отказа кандидатов разрешён CPU fallback.
|
||
CPU decoder: thread_count=4, FF_THREAD_SLICE, без кадровой многопоточности.
|
||
Реальный параллелизм зависит от slices/WPP bitstream; H.264 с единственным slice не даёт четыре независимые задачи.
|
||
Desktop GPU decode: D3D11VA на Windows, VAAPI на Linux, затем CPU. Backend проверяется paced-пробой
|
||
восьми независимых KEY с точными PTS, выдачей до EOS и чтением всех восьми выходов.
|
||
Минимальная сборка FFmpeg v5 явно включает h264/hevc_d3d11va2 либо h264/hevc_vaapi.
|
||
Для Windows DLL старого профиля необходимо запустить tools/chatgui/ffmpeg/build_ffmpeg.sh и пересобрать GUI.
|
||
Android предпочитает аппаратные MediaCodec, проверяет pipeline при paced input и включает заявленный FEATURE_LowLatency.
|
||
Числом внутренних CPU threads MediaCodec управляет платформа; параметры FFmpeg к нему не применяются.
|
||
|
||
Обычная пауза/EAGAIN не теряет кадры и не запрашивает KEY. Реальный отказ decoder/device логируется
|
||
с числом потерянных аппаратных inputs и сохранённых ready кадров; после GPU fault используется CPU и новый KEY.
|
||
Ещё не принятый KEY при отказе открытия GPU повторяется на CPU без потери. Отказ самого устройства
|
||
может уничтожить уже принятые им references/inputs; гарантии короткого backpressure к такому отказу не относятся.
|
||
Watchdog 2с проверяет output progress только без готового/ожидающего показа и при достаточном числе decoder inputs.
|
||
|
||
Диагностика call=debug: playout queued/reserve/awaiting/lead/speed/cadence, RX submitted/presented/encoded/inflight/total;
|
||
video decoder pipeline показывает backend, threads/active_thread_type, submit/decode/GPU transfer/RGB conversion.
|
||
Тесты: 16 сценариев 480/480 с паузами 50–500мс, фиксированный deadline после starvation, снижение 1.596x -> 1.004x;
|
||
отложенные outputs и EOS между поколениями, CPU fallback, настоящие H.264/H.265 bitstream и rate switches.
|
||
Linux Qt RX+GUI: 90/90 без KEY/reject, normal ACK p50=33.4мс/p95=37.7мс, stop=2.1мс;
|
||
с RX80мс/GUI60мс: 90/90, p95=39.1мс, максимальная пауза94.4мс, stop=5.0мс.
|
||
Эти числа относятся к локальному тесту с имитацией paint ACK, не Windows драйверу или панели.
|
||
Собраны Linux GUI и Android APK; Android unit: 108/108. Call transport, RX port и bounded flush проходят.
|
||
В заключительном общем CTest три call-video набора и stream_publish прошли; stream_view один раз
|
||
зафиксировал рост silenceGaps 2 -> 4 в realH264RenderingAndClose. Повтор того же бинарника без изменений
|
||
прошёл (15/15); stream_view/его audio clock этим изменением не редактировались. Эту временную
|
||
нестабильность не скрываем изменением допусков теста. Живой Windows GPU/timer/scanout пока не проверен.
|
||
|
||
Android: компоновка звонка и поворот передачи, 19.10.2026
|
||
Ник находится рядом со стрелкой. В обычном режиме миниатюры занимают место аватара;
|
||
TextureView локального и удалённого видео сохраняются при разворачивании. Их положение
|
||
берётся из измеренного слота; заполнение экрана не добавляет поворот на 90 градусов.
|
||
Панель камеры/кодека/диапазона битрейта/разрешения использует общие CaptureControls со стримом.
|
||
Она появляется при любом нажатии кнопки видео, в том числе во время проверки кодеков,
|
||
при отказе запуска и отказе в разрешении камеры; сохраняется после выключения передачи.
|
||
Первый настоящий TX-кадр также открывает панель. Недоступные опции заблокированы, причина
|
||
отказа показана под строкой; доступные настройки можно менять до повторной попытки.
|
||
Открытое меню приостанавливает автоскрытие. Показ панели сохраняется при пересоздании экрана звонка.
|
||
|
||
CallVideoEngine владеет OrientationEventListener вместе с foreground-сервисом; сворачивание
|
||
экрана не прекращает отслеживание. Углы датчика направлены противоположно display rotation:
|
||
поворот пикселей = sensor + (front ? -deviceAngle : deviceAngle), нормализованный к 0/90/180/270.
|
||
Первый достоверный угол принимается сразу; следующие — с гистерезисом 60 градусов и выдержкой
|
||
250мс. Неопределённое положение сохраняет последний угол. Экран и локальное превью не следуют
|
||
датчику: геометрия превью использует SENSOR_ORIENTATION и фиксированный display rotation.
|
||
|
||
ImageReader сохраняет размер режима камеры. CallVideoCodec поворачивает Y/U/V непосредственно
|
||
при копировании в flexible input MediaCodec, с учётом crop/rowStride/pixelStride и доступных
|
||
границ. В потоке rotation=0: пиксели уже ориентированы. lib/video_plane.c собирается с -O2
|
||
также в debug APK; без оптимизации на Q8 портретный 720p вход занимал около 48мс вместо 27мс.
|
||
Это замер синтетического YUV -> encoder, не гарантия FPS живого звонка.
|
||
|
||
Поворот на 180 градусов при прежних выходных размерах сохраняет codec/generation и запрашивает
|
||
IDR. Перестановка ширины/высоты меняет поколение: raw входы приостановлены, старый encoder
|
||
полностью drained, готовые выходы сохранены до принятия портом, затем ожидается tx_idle.
|
||
Только после этого публикуется новый формат; подача ждёт configured_generation. tx_idle
|
||
означает свободный mailbox независимо от can_encode; уже принятый transport FIFO сохраняет порядок.
|
||
Камера/preview при одном повороте не перезапускаются. Откат настроек получает новое поколение,
|
||
PTS и frame_id продолжаются; неверный режим диагностируется, при полном отказе останавливается TX.
|
||
|
||
CAPS сохраняют формат wire. Общий call_video_limit_orientations расширяет обе осевые границы
|
||
только после реальной landscape+portrait пробы; предел pixels_per_second остаётся прежним.
|
||
Android encoder проверяет обе ориентации каждого предлагаемого размера; desktop encoder —
|
||
1280x720 и 720x1280 перед объявлением возможностей. Независимые Android/desktop decoder probes используют
|
||
настоящие 1280x720 и 720x1280 KEY; портретные fixtures воспроизводит tools/generate_call_video_portrait.py.
|
||
|
||
Проверки: test_video_plane (четыре поворота, stride 1/2, interleaved U/V, смещения и точные границы),
|
||
test_call_video (портретные CAPS/KEY), test_call_video_port (tx_idle отдельно от can_encode),
|
||
CallVideoOrientationTest (направление углов, гистерезис, выдержка, flat/отмена).
|
||
Аппаратный debug probe: adb -s SERIAL shell am instrument -w -e rotations 1
|
||
com.utun.chat/com.utun.chat.data.CallVideoProbe. Он проверяет цветные квадранты Y/U/V после
|
||
настоящего encode/decode, все PTS, четыре поворота и 180 градусов внутри одного encoder.
|
||
Результат — INSTRUMENTATION_CODE: 0; подробности и wait/copy/queue timings — tag CallVideoProbe.
|
||
В живом звонке call=debug выводит submitMaxUs/inputWaitMaxUs/copyMaxUs/queueMaxUs раз в секунду.
|
||
|
||
Согласование возможностей и показ опций после отказа проверены сборками Android/core/chatgui.
|
||
Android unit: 111/111; шесть native video наборов: 6/6; Qt encoder/clock CTest: 2/2.
|
||
Qt bothOrientations кодирует и декодирует H.264/H.265 при переходах 1280x720 -> 720x1280 -> 1280x720,
|
||
проверяя поколения и PTS. Аппаратный probe на SM и Q8 проходит с INSTRUMENTATION_CODE: 0;
|
||
дополнительно выбирает реальные проверенные режимы в обеих ориентациях через общий C CAPS/JNI.
|
||
Это локальные codec-проверки, живой межплатформенный звонок новой пары сборок ещё не проверен.
|
||
|
||
Windows 2.log, неравномерность показа, 19.10.2026
|
||
На 136 стабильных секундных интервалах shown/interval даёт 30.09fps; drop_order=0,
|
||
обычный общий запас — 2-3 кадра. В живом потоке выбран D3D11VA, а threads=1 относится
|
||
к аппаратному backend. Это не однопоточный программный decoder.
|
||
|
||
Сравнение устойчивых участков 21:40:51-21:41:37 (480x640) и 21:41:55-21:42:31 (720x1280):
|
||
медиана секундных decode_max_us растёт 4450 -> 10111, convert_max_us — 2229 -> 6744,
|
||
dispatch_max_us — 3079 -> 7311. При этом wake_late_max_us — 1076 -> 1045,
|
||
GUI paint_max_us — 3505 -> 2947, notification_wait_max_us — 434 -> 422.
|
||
Это медианы максимумов окон, а не средние длительности отдельных кадров.
|
||
|
||
RX worker выполняет decoder.submit/receive (включая GPU transfer/RGB/rotation) в том же
|
||
потоке, что и playout.poll/publishImage. Готовая голова может пропустить назначенный срок
|
||
во время подготовки следующего кадра. Для прямой проверки добавлена секундная диагностика:
|
||
decode_deadline_misses — число операций decoder, начавшихся до срока готовой головы и
|
||
завершившихся после него; decode_deadline_max_us — наибольшее такое опоздание;
|
||
decode_deadline_stage=input/output, work_us и margin_us описывают именно эту операцию.
|
||
Ожидание аудио, GUI ACK и отсутствие назначенной готовой головы исключены из счётчика.
|
||
Фильтр — call=debug; алгоритм, лимиты очередей и порядок кадров не менялись.
|
||
|
||
Есть отдельная граница достоверности: desktop ACK отправляется в конце paintEvent,
|
||
до подтверждённой презентации окна. По этим метрикам нельзя измерить scanout или признать
|
||
доказанной потерю кадров композитором. Текущая формула срока следующего кадра использует
|
||
предыдущий ACK плюс шаг минус сглаженный dispatch lead; индивидуальные задержки рисования
|
||
тем самым влияют на расписание. Для устранения зависимости нужны независимое исполнение
|
||
decoder/планировщика и синхронизация presentation с обновлением дисплея; обычный таймер
|
||
и ранний ACK из QPainter не дают такой гарантии.
|
||
|
||
Независимый desktop-планировщик, 19.10.2026 — реализовано
|
||
TX по-прежнему владеет encoder. RX worker теперь единолично владеет decoder, всеми
|
||
submit/receive/EOS/open/close, GPU transfer, RGB и rotation. Отдельный display worker
|
||
владеет call_video_playout и не вызывает decoder, port_status/pull, Qt post/invokeMethod,
|
||
не ждёт GUI и не форматирует/записывает логи.
|
||
|
||
Передача RX -> display: SPSC кольцо из трёх неизменяемых Picture. Producer заполняет слот
|
||
до release-публикации; consumer освобождает его только после ACK выбранного кадра.
|
||
Голова во время GUI ACK остаётся в кольце и входит в его ёмкость. При полном кольце
|
||
останавливается подача decoder; до восьми входов разрешают проверенный hardware pipeline.
|
||
Старый QImage в слоте заменяет decoder, освобождение его буфера не попадает в display.
|
||
Смена поколения сначала выводит принятые decoder-входы; старые готовые изображения
|
||
сохраняют собственные PTS/формат и показываются до новых.
|
||
|
||
GUI читает один опубликованный слот по QWinEventNotifier (Windows) или QSocketNotifier
|
||
(POSIX pipe). Таймер записывает metadata и serial, затем сигналит событие; аллокации
|
||
queued Qt lambda в нём больше нет. Только GUI копирует shared QImage и вызывает imageChanged.
|
||
ACK имеет глобально возрастающий serial; дубликаты и ACK предыдущего start не освобождают
|
||
слот новой сессии. stop запрещает публикации, будит и присоединяет display/RX/TX независимо
|
||
от GUI ACK, затем закрывает порт. Notifier удаляется до закрытия native handles.
|
||
|
||
Общий запас: cumulative port_received - completed - decoder_discarded; переносы между
|
||
encoded/decoder/ready/GUI не меняют счётчик. port_received читается атомарно, без mutex
|
||
порта, который может быть занят копированием нового encoded кадра. Decoder-discard
|
||
меняется только при настоящем отказе цепочки/codec; короткая пауза ничего не отбрасывает.
|
||
call_audio_try_clock использует try_lock и на call_audio, и на jitter mutex. BUSY вызывает
|
||
повтор через 500мкс без сброса назначенной головы; экстраполяции устаревшего audio PTS нет.
|
||
|
||
У display/RX отдельные wake: Windows event + high-resolution timer, POSIX nonblocking pipe
|
||
и pselect. notify не берёт пользовательский mutex. Runtime ошибки wake передаются RX
|
||
для вывода и остановки workers. Логи playout копируются в bounded SPSC очередь; форматирует
|
||
их RX. Переполнение диагностической очереди считается и явно логируется, кадры не вытесняются.
|
||
|
||
Фаза опирается на запланированную презентацию и корректируется по ACK на error/8,
|
||
не более 1мс за кадр. Настоящая пауза более половины периода привязывает фазу к новому ACK;
|
||
минимальный плановый интервал ограничивает мгновенный burst темпом 1.6x. Большие разрывы
|
||
capture PTS не проигрываются повторно. Это не VSync: ACK текущего окна всё ещё paintEvent,
|
||
а фактическая презентация Windows-композитором требует отдельного render backend.
|
||
|
||
Проверки: C playout — 480/480 при сетевых/CPU паузах 50/100/200/500мс, возврат темпа,
|
||
смена render latency 3->9мс без накопленного сдвига (phase=11625мкс).
|
||
Qt clock — три сценария 90/90, смена поколения на 45-м кадре, RX80/GUI60мс, audio BUSY,
|
||
decoder output+12мс, stop без ACK, запоздалый ACK после start, 100000 конкурентных публикаций.
|
||
В последнем Linux-прогоне p95 показа: обычный 34.7мс, с медленным decoder 35.2мс;
|
||
максимумы работы display в секундных окнах 26-143мкс. Это локальный замер, не Windows scanout.
|
||
Qt encoder/GPU/clock/jitter: 4/4 PASS, core и vibechat собраны.
|
||
Полный check.sh вне sandbox: 163/164; известный test_encrypted_attachment снова phase=5,
|
||
heartbeats=12001 (описан в doc/tasks.md). Лог: /tmp/video-independent-check-full.log.
|
||
|
||
Full HD и режимы камеры, 19.10.2026
|
||
Предел интерфейса звонка — 1920x1080 либо 1080x1920, 30fps. Начальный режим и диапазон
|
||
300k..4M не повышаются автоматически. Общие wire CAPS уже допускают эти геометрии.
|
||
Для Full HD@30 согласуются AVC level_idc=40 и HEVC general_level_idc=120, Main tier;
|
||
нижние размеры по-прежнему получают минимальный допустимый уровень через call_video_choose.
|
||
|
||
Desktop camera worker опрашивает QCameraDevice::videoFormats и использует те же
|
||
stream_video_add_camera_modes/stream_video_camera_supports, что publisher трансляций.
|
||
В звонок попадают только чётные аппаратные размеры до Full HD при 30/29.97fps.
|
||
Дубликаты pixel format объединяются; список передаётся в GUI вместе с id/именем камеры
|
||
и обновляется по videoInputsChanged. Драйвер опрашивается только в отдельном процессе.
|
||
GUI сохраняет разрешение при смене камеры, если оно поддерживается; иначе выбирает 640x480
|
||
либо минимальный доступный размер. Отсутствие режимов не превращается в запрос размера -1.
|
||
При старте требуется точное совпадение аппаратной геометрии; другой размер готового
|
||
QImage диагностируется как ошибка вместо скрытого масштабирования.
|
||
|
||
Shared mapping FRAME/ACK расширен до 1920*1080*4 байт; обе стороны проверяют геометрию.
|
||
Worker копирует RGB32 построчно с учётом stride, GUI получает собственный QImage.
|
||
Владение mapping, поколения и ограничение единственного кадра в IPC сохранены.
|
||
|
||
Android выбирает 640x480/1280x720/1920x1080 из YUV_420_888 камеры, учитывая min frame
|
||
duration и диапазон AE с upper=30. Каждая геометрия кодера проверяется в двух ориентациях;
|
||
для разных размеров могут использоваться разные аппаратные/программные реализации.
|
||
Decoder также хранит реализацию и измеренный pipeline отдельно для 720p и Full HD.
|
||
Это сохраняет работающий GPU на 720p, если Full HD проверен только через CPU.
|
||
CAPS содержат реальные проверенные encode/decode пределы; выбранный режим дополнительно
|
||
проверяется открытием codec и bitstream, как требует контракт независимых верхних границ.
|
||
|
||
Общие KEY fixtures дополнены 1920x1080 и 1080x1920, AVC Baseline/CB и HEVC Main.
|
||
call_video_probe_key принимает width/height; генератор tools/generate_call_video_portrait.py
|
||
--fullhd явно задаёт high-tier=0. Нельзя заменять Full HD-пробу одной лишь проверкой 720p.
|
||
Desktop capability probes отменяются при stop, включая ожидание другого probe;
|
||
незавершённый результат не кэшируется. Codec/драйверные вызовы выполняются вне GUI/playout.
|
||
|
||
Проверки: native bitstream/геометрия/профили/DPB — PASS; Qt encoder/GPU/clock/camera/audio
|
||
5/5 PASS, включая настоящие Full HD encode/decode в обеих ориентациях, поколения,
|
||
границу shared buffer и stop во время проб. Общие camera modes publisher — 3/3 PASS.
|
||
Ядро, все desktop targets и Android APK собраны; Android unit 111/111 PASS.
|
||
Логи: /tmp/fullhd-native-video-test.log, /tmp/fullhd-qt-verified-tests.log,
|
||
/tmp/fullhd-shared-camera-modes.log, /tmp/fullhd-android-final-build.log,
|
||
/tmp/fullhd-desktop-final-build.log. Полный check.sh — 163/164, прежний отказ
|
||
test_encrypted_attachment phase=5/heartbeats=12001: /tmp/fullhd-core-check.log.
|
||
Это codec/IPC-проверки; живой Full HD звонок новой сборки на Windows/Android ещё не проверен.
|
||
|
||
Samsung HEVC: сравнение звонка и трансляции, 19.10.2026
|
||
Свежий звонок на SM_A525F/RZ8R72MG64B: c2.qti.hevc.encoder и его OMX alias
|
||
на 640x480/1280x720/1920x1080 возвращают profile=1, tier=1 вместо запрошенного
|
||
Main tier. Отказ происходит в hevc_ptl общего C validator, до публикации encoder CAPS.
|
||
Приёмный c2.qti.hevc.decoder независимо прошёл реальные пробы до Full HD.
|
||
Лог: /tmp/android-sm-hevc-recall.log, 00:05:08.731..00:05:09.307 времени телефона.
|
||
|
||
В Android publisher трансляции используется Surface, задаются profile/bitrate/fps,
|
||
но не KEY_LEVEL, KEY_BITRATE_MODE или KEY_LATENCY. StreamVideoCodec проверяет
|
||
envelope/NAL-набор, затем viewer передаёт VPS/SPS/PPS настоящему decoder. Строгого
|
||
Main-tier verifier звонка на этом пути нет. Это объясняет доступность HEVC в
|
||
трансляции; отдельной нормализации Qualcomm SPS/VPS в трансляции нет.
|
||
|
||
Добавлен debug-only CallVideoHevcProbe: отдельный app_process без Camera2, сетевого
|
||
ядра, установки APK или перезапуска живого приложения. Он сравнивает 54 варианта:
|
||
640x480/480x640/1280x720/720x1280/1920x1080/1080x1920, YUV/Surface,
|
||
явный/автоматический level, CBR/режим по умолчанию, плюс точные настройки publisher.
|
||
Каждый вариант кодирует восемь кадров с проверкой порядка PTS, сохраняет исходный
|
||
KEY и Annex B, отдельно вызывает общий validator и аппаратный decoder.
|
||
Отказ validator не отменяет эксперимент decode. Параметр parameters вместо матрицы
|
||
опрашивает supportedVendorParameters и типы параметров (API 31+).
|
||
|
||
Запуск после assembleDebug: скопировать APK и все lib/arm64-v8a/*.so из этого APK
|
||
в отдельный /data/local/tmp/utun-hevc-probe на телефоне; сам APK сделать chmod 444.
|
||
Пример команды adb shell (OUTPUT — отдельный каталог результатов):
|
||
CLASSPATH=/data/local/tmp/utun-hevc-probe/probe.apk app_process \
|
||
-Djava.library.path=/data/local/tmp/utun-hevc-probe / \
|
||
com.utun.chat.data.CallVideoHevcProbe OUTPUT [parameters]
|
||
Зависимость libonnxruntime.so необходима для загрузки JNI validator, хотя сама проба
|
||
ONNX не использует. Инструмент требует Android API 29+ и аппаратные HEVC encode/decode.
|
||
|
||
Результат Samsung: 54/54 encode и hardware decode, по восемь кадров, без потерь PTS;
|
||
общий validator отвергает все 54 потока. Независимый FFmpeg decode с -xerror также
|
||
вернул восемь кадров для всех 54 файлов без ошибок. trace_headers подтверждает:
|
||
всегда tier=1 и reorder=0; 16 явных режимов имеют level=93, восемь Full HD — 120,
|
||
все 30 режимов без явного KEY_LEVEL имеют level=30, включая Full HD.
|
||
Следовательно, снятие KEY_LEVEL, CBR или переход на Surface не исправляют HEVC
|
||
звонка. Автоматический level дополнительно не соответствует большой геометрии.
|
||
Опрос vendor-параметров обнаружил только bitrate-mode, preprocess-rotate,
|
||
timestamp-source-avtimer и raw.blurinfo; переключателя tier среди них нет.
|
||
Это короткая синтетическая проверка совместимости, не проверка bitrate/VBV на камере.
|
||
|
||
Артефакты: /tmp/hevc-probe-sm/ (54 KEY, 54 Annex B, results.log),
|
||
/tmp/hevc-probe-sm-ffmpeg.log, /tmp/hevc-probe-sm-headers.log,
|
||
/tmp/hevc-probe-sm-parameters.log. Сборки ядра и assembleDebug прошли:
|
||
/tmp/hevc-probe-core-build.log, /tmp/hevc-probe-android-build.log.
|
||
|
||
Рабочий контракт звонка пока сохранён: Main tier; HEVC TX Samsung не включён.
|
||
Предлагаемый следующий этап исправления: явный HEVC level 4 для Qualcomm,
|
||
поддержка High tier с отдельным согласованием encode/decode возможностей и
|
||
настоящими High-tier decode fixtures на обеих платформах, сохранением общих
|
||
ограничений bitrate/геометрии/DPB/reorder. Простое снятие запрета tier оставляет
|
||
непроверенный приём и ошибочные low-level заголовки. Модификация VPS/SPS не выполнялась.
|
||
|
||
HEVC High tier для Samsung, 19.10.2026
|
||
Реализовано явное согласование двух режимов HEVC Main: бит 1 — Main tier,
|
||
бит 2 — High tier. Это значения call_video profiles/format.profile, не HEVC Main 10.
|
||
High tier принимается только с level_idc>=120 (level 4), Main tier предпочтителен,
|
||
когда у обеих сторон есть оба режима. AVC Baseline/CB и размеры wire структур сохранены.
|
||
Бюджеты bitrate/HRD/CPB остаются консервативными Main-tier, максимум target — 4M.
|
||
Нормативное ограничение tier/level: ITU-T H.265, Annex A, general_tier_flag:
|
||
https://www.itu.int/rec/dologin_pub.asp?id=T-REC-H.265-201304-S%21%21PDF-E&lang=e&type=items
|
||
|
||
Общий KEY validator различает tier и проверяет согласованный режим по VPS/SPS.
|
||
High-tier decoder fixtures получают декларацию High tier/level 4 из известных
|
||
Main-tier fixtures: их coding tools, DPB и BR/CPB укладываются в более высокий уровень.
|
||
Меняются только PTL эталонных проб; производственные VPS/SPS не переписываются.
|
||
Android и desktop проверяют декодирование обоих режимов отдельно, в обеих ориентациях,
|
||
на 720p и Full HD; отказ одного режима не снимает уже проверенную поддержку другого.
|
||
Desktop TX продолжает объявлять только реально проверенный Main tier.
|
||
|
||
Android сначала пробует прежний level, затем HEVC level 4; найденные profile/tier/level
|
||
сохраняются для конкретного размера. Live выбирает именно режим проверенного encoder.
|
||
Запрос platform Main tier одинаков в probe и live: Samsung при MainTierLevel4 выдаёт
|
||
High tier, а при HighTierLevel4 в эксперименте выдал Main tier. Поэтому нельзя строить
|
||
Android KEY_LEVEL из полученного wire profile/tier и считать его обратным отображением.
|
||
prepare проверяет реальный поток, а production-проба требует точного совпадения с mode.
|
||
|
||
CallVideoHevcProbe OUTPUT call проверяет настоящий CallVideoCodec: probe/prepare,
|
||
восемь кадров, точные PTS, in-place 1M -> 4M -> 300k -> 4M и decode каждой фазы.
|
||
На SM_A525F прошли все 24 фазы: 640x480/1280x720/1920x1080 и обратные ориентации,
|
||
c2.qti.hevc.encoder, Main/High tier, level 4. Это синтетический roundtrip, не замер
|
||
среднего bitrate на сложной сцене. Все 24 настоящих KEY дополнительно прошли
|
||
production CallVideoDecoder на desktop (четыре CPU slice threads) и c2.unisoc.hevc.decoder
|
||
второго Android; режим receive берёт *.key из OUTPUT и проверяет восемь PTS на файл.
|
||
Артефакты: /tmp/hevc-high-sm-production/, /tmp/hevc-high-desktop-cross.log,
|
||
/tmp/hevc-high-q8-receive.log. Живой звонок камеры новой сборки ещё не проверен.
|
||
|
||
Проверки: native call_video — PASS; Android assembleDebug и unit — PASS;
|
||
desktop собран целиком; encoder/GPU/camera — 3/3 PASS, clock — PASS.
|
||
Camera IPC проверен вне sandbox, внутри него QLocalServer::listen запрещён.
|
||
Полный check.sh: 162/164; прежний test_encrypted_attachment и нестабильный
|
||
test_local_transport (отдельный повтор PASS). Логи: /tmp/hevc-high-core-check.log,
|
||
/tmp/hevc-high-local-transport-retry.log, /tmp/hevc-high-native-test.log,
|
||
/tmp/hevc-high-android-final-build.log, /tmp/hevc-high-qt-final-tests.log.
|
||
Для согласования High tier необходимо обновить обе стороны звонка. Установленные
|
||
приложения в ходе проверки не заменялись: app_process выполнялся отдельно.
|
||
|
||
До 60 fps и восстановление presentation, 20.10.2026
|
||
Причина «нет подходящих режимов» в Windows 1.log: камера объявила фиксированные
|
||
640x360@330, 1280x720@100, 1920x1080@50, а прежний фильтр требовал диапазон с ровно 30 fps.
|
||
Теперь FPS пользовательского режима означает частоту передачи. Capture остаётся аппаратным;
|
||
общий со стримами отбор разрешает более быстрый источник. На desktop в IPC и combo передаются
|
||
пары размер/FPS; выбирается наименее быстрый подходящий аппаратный формат без изменения его частоты.
|
||
До RGB-конвертации/IPC/кодирования работает фазовый отбор raw кадров с допуском четверти периода.
|
||
Фаза не сбрасывается на обычном рассогласовании частот (например 60 -> 50), только при отставании
|
||
более одного периода. Долгая пауза не создаёт немедленный догоняющий burst исходных кадров.
|
||
|
||
Android пересекает пары размер/FPS камеры с проверенными encoder modes для 30/50/60 fps.
|
||
Учитываются YUV minFrameDuration и AE ranges; выбирается минимальный верхний AE предел >= FPS.
|
||
Preview использует вторую поверхность той же сессии. Изменение размера/FPS перезапускает capture;
|
||
поворот и bitrate продолжают прежний жизненный цикл. Недоступная пара отклоняется с notice/логом.
|
||
Частота передачи не понижается регулятором задержки. Backpressure отбирает только ещё не закодированный raw.
|
||
|
||
Encoder/decoder проходят отдельные реальные пробы 30/50/60 fps в обеих ориентациях.
|
||
Для AVC 720p50/60 нужен level 3.2, Full HD50/60 — 4.2; для HEVC — 4 и 4.1 соответственно.
|
||
Android probe/live используют проверенный level, Main-tier platform request и фактический wire tier.
|
||
FPS и PPS в CAPS агрегируются по настоящим пробам независимо: 1080p30 +720p60 не объявляют 1080p60.
|
||
GPU decoder допускается только по подтверждённому размеру/FPS/профилю, CPU сохраняет четыре slice threads.
|
||
Эталонные KEY 60 fps: call_video_fast_fixtures.h, генератор --fast. High tier не понижает level 4.1 до 4.
|
||
Android подготовка отменяется при завершении звонка; незавершённые режимы не публикуются.
|
||
|
||
Все encoded кадры последовательно подаются в decoder; готовая очередь вместе с ожидающим ACK <=3.
|
||
Сначала общий playout пытается сократить долг ускоренным показом. При входе 60 fps и экране 60 Hz
|
||
ускориться физически нельзя. Если лаг сверх рабочего запаса не сокращается за 500мс хотя бы на
|
||
четверть периода, DROP_LATE освобождает только decoded presentation до возврата к запасу.
|
||
Рабочая граница — (reserve+1)*период +50мс, где reserve включает обязательный pipeline decoder.
|
||
Сохраняются минимум голова/будущие кадры. DROP_LATE не очищает decoder, не запрашивает KEY и не меняет
|
||
last_show/last_deadline: часы продвигает только ACK. На desktop решение и освобождение SPSC-слота
|
||
выполняются отдельным timer без codec, GUI, блокирующих mutex или синхронного форматирования логов.
|
||
Текущая desktop версия пропускает показ после GPU readback/RGB; перенос этих операций из RX сюда не выполнялся.
|
||
|
||
Диагностика call=debug: camera capability/selected capture_fps/output_fps, camera flow frames/paced,
|
||
decoder probe size/fps/backend, video playout drop_display; INFO video display catchup объясняет
|
||
lag/recovered/window/queued/reserve. Статус desktop и Android меню показывают выбранный FPS.
|
||
|
||
Проверки: Android APK и 114 unit — PASS; desktop build, Qt encoder/GPU/clock/camera/stream modes — 5/5 PASS.
|
||
Native playout: малые/большие паузы с возможностью ускорения сохраняют все кадры; на модели 60Hz после
|
||
пауз 50/100/500мс декодированы все 600, пропущен только показ 1/4/29 кадров, лаг возвращается к ~67мс.
|
||
Настоящий desktop RX: 180/180 decoded при GUI freeze 500мс, 149 показано, KEY/reject=0; timer не вызывает codec/port mutex.
|
||
Samsung: 72 production encode/decode фазы (три размера x30/50/60 xдве ориентации xчетыре bitrate), PASS.
|
||
Все 72 настоящих KEY прошли production desktop decoder с точными FPS/PTS. Unisoc проверил 24 собственные
|
||
encode/decode фазы 640x480/480x640 при 30/50/60; более высокие TX режимы не объявлены.
|
||
Тот же Unisoc аппаратно принял все 72 Samsung KEY, включая Full HD60/High tier/level 4.1,
|
||
по восемь кадров с точными PTS; /tmp/video60-q8-receive-results.log, remote finished files=72.
|
||
Полный check.sh: 163/164, только прежний test_encrypted_attachment phase=5/heartbeats=12001;
|
||
артефакт /tmp/utun_encrypted_attachment_25Uyql. test_local_transport на этот раз PASS.
|
||
Логи: /tmp/video60-full-check.log, /tmp/video60-playout-test.log, /tmp/video60-qt-tests.log,
|
||
/tmp/video60-android-final-build.log, /tmp/video60-desktop-cross.log; артефакты Samsung /tmp/video60-sm-results/.
|
||
Живой звонок новой камеры на Windows не проверен. Установленные приложения не заменялись;
|
||
аппаратные проверки выполнялись отдельным app_process. Обе стороны нужно обновить для новых режимов.
|
||
|
||
Android Surface ACK: устранение фризов 1–2 fps на Samsung, 20.10.2026
|
||
Свежий logcat /tmp/video-freeze-sm.log, звонок 8563820959416855278, 01:31:23–01:32:39:
|
||
77 playout сводок, медиана 1.99 показанных fps, медиана dispatch-to-ACK 538мс, максимум 1019мс.
|
||
Очередь ready=2, awaiting=1; decode останавливается за ожидающим Surface ACK. Смена размера/FPS
|
||
не устраняет паузы. Это конкретное ожидание подтверждения GUI, не доказательство медленного decoder.
|
||
|
||
ViewRootImpl в performDraw захватывает список frame commit callbacks ДО draw TextureView.
|
||
Прежний onSurfaceTextureUpdated вызывался уже во время draw и регистрировал callback слишком поздно.
|
||
Он оставался до следующего постороннего UI draw (статистика каждые 500мс). RX ждал ACK,
|
||
не отправлял следующий кадр на Surface, а без нового кадра TextureView сам не создавал следующий draw.
|
||
Отсюда цикл ожидания и накопление задержки; пропуск decoded presentation не мог устранить причину.
|
||
|
||
CallVideoTexture теперь регистрирует frame commit в OnPreDrawListener, до захвата списка ViewRoot.
|
||
Timestamp SurfaceTexture читается после commit того же кадра и сверяется с единственным ожидающим PTS.
|
||
Посторонний draw со старым timestamp не подтверждает новый кадр: настоящий TextureView update сам
|
||
назначит следующий draw/commit. На API<29 сохраняется postOnAnimation с ожиданием правильного timestamp.
|
||
Listener снимается при detach; старые callback проверяют закрытие и идентичность Surface.
|
||
Нет постоянного invalidate-цикла, дополнительного draw после каждого кадра и ACK по таймауту.
|
||
Encoded порядок, три decoded места, playout/rate/protocol не менялись.
|
||
Диагностика call=debug: video surface updates/commits/updateToCommitMaxUs/pts и
|
||
video playback acknowledged/surfaceAckMaxUs/surfaceWaitMs разделяют texture update, commit и RX ACK.
|
||
Документация API: https://developer.android.com/reference/android/view/ViewTreeObserver#registerFrameCommitCallback(java.lang.Runnable)
|
||
|
||
VideoSurfaceProbeActivity в отдельном audio-probe APK проверяет настоящий TextureView/EGL:
|
||
один EGL-кадр до ACK, точные presentation timestamps, посторонний UI draw раз в 500мс.
|
||
Команда: adb shell am start -n com.utun.audioprobe/.VideoSurfaceProbeActivity --ei fps 60 --ez legacy false
|
||
legacy=true воспроизводит прежнюю регистрацию в onSurfaceTextureUpdated.
|
||
На Samsung старая схема: 15 ACK /8.028с, максимальное ожидание 516414мкс — дефект воспроизведён.
|
||
Исправленная схема 60fps: 483 ACK /8.050с, максимум 21391мкс; все ACK соответствуют ожидающим PTS.
|
||
30fps: 241 ACK /8.033с, максимум 31071мкс. Три посторонних draw увидели старый timestamp
|
||
и правильно дождались настоящего обновления; это не потеря кадра и не неверный ACK.
|
||
Тест проверяет FPS и число подтверждений/входов, а не отсутствие законных промежуточных UI draw.
|
||
Лог: /tmp/video-surface-sm-probe.log. Проба второго телефона не получила Surface при Dozing;
|
||
её остановили, устройство не разблокировали, результатом проверки показа это не считается.
|
||
|
||
C-сборка, Android assembleDebug и 114 unit — PASS. Полный check.sh: 162/164, прежние сбои:
|
||
local_transport peers():95 после TTL age_ms=30335 (отдельный повтор PASS), encrypted_attachment
|
||
phase=5/heartbeats=12001, артефакт /tmp/utun_encrypted_attachment_3XZGxj.
|
||
Логи: /tmp/video-surface-freeze-core-build.log, /tmp/video-surface-freeze-android-final.log,
|
||
/tmp/video-surface-freeze-full-check.log, /tmp/video-surface-freeze-local-retry.log.
|
||
Основное приложение на телефонах в этой проверке не заменялось; готовый исправленный APK —
|
||
tools/chatgui-android/app/build/outputs/apk/debug/app-debug.apk. Сетевой звонок с новым APK ещё не проверен.
|