You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
535 lines
56 KiB
535 lines
56 KiB
Видеозвонки Android и ChatGUI |
|
|
|
Контракт |
|
Каждая сторона независимо включает видео. Автовыбор H.265 Main 8-bit, затем |
|
H.264 Baseline/Constrained Baseline: пересечение encoder отправителя и decoder получателя. |
|
Encode/decode проверяются независимо, профили AVC согласуются явной маской. |
|
HEVC Main/Main tier, 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. |
|
Камера, разрешение, кодек и диапазон битрейта меняются во время звонка. |
|
Автоматический диапазон по умолчанию 300–4000 кбит/с (десятичные единицы). |
|
|
|
Время |
|
Общая монотонная шкала захвата аудио и видео на каждом отправителе. PTS первого |
|
аудиосэмпла/кадра сохраняется до вывода; время сетевой отправки учитывается отдельно. |
|
Приёмное видео следует исходной позиции реально воспроизводимого аудио, включая |
|
SoundTouch и очередь устройства. Алгоритм регулирования аудиобуфера сохраняется. |
|
Искусственная тишина/тоны не продвигают исходную позицию. Формат видео меняется по PTS; |
|
новое поколение не уничтожает старые кадры, соответствующие ещё не прозвучавшей речи. |
|
|
|
Передача и регулирование |
|
Один A/V-планировщик, независимые логические каналы управления/аудио/видео. |
|
Звук продолжает работу при отказе камеры. Ограничены ожидание encode, очереди, |
|
размер сборки кадра и память получателя; ACK не равен успешному decode/render. |
|
Роутер считает исходный payload и пакеты, видеомодуль вычитает фиксированный |
|
заголовок фрагмента. Физические очереди повторно не суммируются с router inflight. |
|
Окно ACK: 10 завершённых интервалов по 50мс, текущая ячейка отдельно. |
|
Регулятор общий C, решения проверяются управляемым временем. Снижение учитывает |
|
подтверждённую скорость и разгрузку задолженности; повышение — малыми пробами. |
|
Нет отдельного неограниченного интегратора: задолженность уже интегрирует дефицит. |
|
Учитываются прогрев, RTT, minrtt_probe, отсутствие маршрута, локальное удержание |
|
планировщиком, применение encoder-команды и смена эпохи. |
|
|
|
Владение |
|
Состояние звонка/регулятора/роутера — uasync. Камера и encoder/decoder — платформенные |
|
workers. Ограниченный mailbox, одно ожидающее уведомление. Команды bitrate имеют |
|
call_id, video_generation и request_id; одно исполнение и последнее желаемое значение. |
|
Принятая настройка не заменяет измерение фактического выхода. Shutdown отменяет |
|
уведомления/таймеры, останавливает producers, затем освобождает очереди и codecs. |
|
|
|
Применение 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 до готовности кандидата. Android переоткрывает |
|
encoder последовательно (аппаратный ресурс может быть единственным), при отказе |
|
восстанавливает прежнюю конфигурацию и подтверждает фактически восстановленный bitrate. |
|
Допустима пауза картинки; аудио и capture timeline продолжаются. Реальные затраты переключения и |
|
пиковый объём IDR нужно проверить для каждого доступного режима/backend. |
|
|
|
Порядок реализации |
|
[+] История первых 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 один незавершённый кадр, на RX одна ограниченная сборка; deadline 2с. |
|
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 по capture PTS создаётся кандидат; |
|
старый encoder сохраняется до получения полного KEY с точным исходным PTS. |
|
При отказе кандидата возвращается 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мс. |
|
|
|
Подтверждено на подключённых устройствах последней 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 автоматически не перезапускался; проверка полного звонка после исправления остаётся. |
|
|
|
Desktop UI: принимаемый кадр раскрывает окно на весь экран с сохранением пропорций. |
|
Своя камера отображается миниатюрой поверх изображения собеседника; при отсутствии |
|
входящего видео — миниатюрой в обычном окне. Без кадров видеоплощадка не занимает места. |
|
Esc/F11, кнопка и двойной щелчок переключают полноэкранный режим; геометрия окна сохраняется. |
|
Настройки камеры/кодека/битрейта/звука и диагностика свёрнуты под «Настройки звонка». |
|
Preview берётся из того же QVideoFrame, отдельная камера не открывается; обновление до 10 fps. |
|
После 5 секунд без новых показанных RX кадров изображение скрывается: отдельного VIDEO_OFF |
|
в протоколе пока нет. Возобновление видео снова открывает полный экран.
|
|
|