Document audited call video and session contracts
This commit is contained in:
1 parent
1899b7f8cf
commit
07f63c3047
13 files changed
+350
-92
No files matched your search
@@ -1659,3 +1659,110 @@ Check запущен от user в отдельном network namespace с IPv6/l
|
||||
Логи /tmp/call-rebase-{native-build,gui-configure,gui-build,android-build,voice-tests,full-check,netns,validation}.log;
|
||||
проверка сохранности /tmp/call-rebase-validate.py, runner /tmp/call-rebase-{check,netns}.sh.
|
||||
Общий аудит остаётся частичным; статусы чек-листа аудиоконтрактов не менялись.
|
||||
|
||||
22. Сверка контрактов call_video — 26.10.2026, utun4, base 1899b7f8
|
||||
|
||||
По запросу пользователя продолжен внешний utun-doc-audit-checklist.txt.
|
||||
Прочитаны полностью шесть реализаций src/call/call_video{,_bitstream,_playout,
|
||||
_port,_rate,_transport}.c и соответствующие заголовки. Четыре fixtures-заголовка
|
||||
проверены извлечением всех массивов и настоящим декодированием. Сверены семь
|
||||
desktop call_video*.h с encoder/decoder/engine и inline queue/wake/capture clock/cadence;
|
||||
для doc/call_video.txt проверены также Android JNI, CallVideoCodec/Engine/Player,
|
||||
Surface ACK и запуск фоновых codec-проб в обоих приложениях.
|
||||
|
||||
Исправлены комментарии шести C- и трёх C++-заголовков, дополнен текущий контракт
|
||||
doc/call_video.txt. Pure validators/serializers доступны codec workers, receiver
|
||||
имеет одного владельца; утверждение «все операции в uasync» относилось к transport.
|
||||
Уточнены единицы/границы format/limits/frame, владение fragment view и полного RX,
|
||||
срок частичной сборки по следующему push, возвраты и неизменность выходов при отказе.
|
||||
KEY envelope/разбор нужных NAL-полей/настоящее decode описаны как разные проверки;
|
||||
общий verifier не декодирует entropy payload. HRD High tier консервативно ограничен Main-tier таблицей.
|
||||
|
||||
Зафиксированы CAPS64/header56, точные поля/смещения/BE; video control uint64
|
||||
по текущему коду native endian, cross-endian совместимость не подтверждается.
|
||||
Configure может изменить локальные настройки до ошибки отправки CAPS; port-команды
|
||||
подтверждают постановку, send=1 — копирование, transport может отвергнуть кадр позже.
|
||||
TX mailbox8x2МиБ, transport32/500мс/2МиБ, RX8/4МиБ; retry сохраняет зависимый хвост.
|
||||
Отражены CAPS_ACK, bounded flush16, send_ready, audio gate, timeout команды12с,
|
||||
порядок close/detach и различия завершения workers desktop/Android.
|
||||
Чистый rate_step допускает100Мбит/с, диапазон звонка ограничивают transport/parser.
|
||||
|
||||
Устранены устаревшие текущие формулировки о полном отсутствии DROP_LATE,
|
||||
port.rx_frames как единственном desktop счётчике, фазе расписания прямо от каждого ACK
|
||||
и condition_variable fallback при полном отказе Windows timer handles.
|
||||
Desktop при недоступных handles отклоняет start; runtime ошибки передаёт RX для остановки.
|
||||
Исторические wire/VBV/DPB/GPU-only/очереди и результаты прогонов оставлены с явной датированной областью;
|
||||
ссылки из начала документа ведут к поздним действующим этапам, а не старому playout1.04x.
|
||||
Уточнено: пользовательская смена режима desktop допускает отмену старого worker batch,
|
||||
а Android геометрия ждёт drain/tx_idle; смена bitrate desktop выводит старый encoder полностью.
|
||||
Новых runtime-дефектов в рамках этой сверки не подтверждено; реализация не менялась.
|
||||
|
||||
Проверки и границы
|
||||
|
||||
Профиль собран штатными целями tests/Makefile: пять наборов test_call_video,
|
||||
test_call_video_rate, test_call_video_playout, test_call_video_port, test_call_video_flush
|
||||
PASS, каждый exit0. Шестой набор test_call —2/2 PASS после отдельной сборки.
|
||||
Первый test_call в sandbox не создал сокеты (Operation not permitted); разрешённый
|
||||
повтор вне sandbox успешен, проверяет настоящие два ядра и видеотранспорт, без камер.
|
||||
Логи /tmp/call-video-doc-audit-{build,call-build,native-results}.log,
|
||||
/tmp/call-video-doc-audit-test_call*.log и соответствующие test_call_video*.log.
|
||||
|
||||
Все16 встроенных KEY прошли системный FFmpeg -xerror: ровно один decoded frame,
|
||||
ожидаемые geometry/profile/level, has_b_frames0. FPS30/60 проверен по VUI timing,
|
||||
для AVC time_scale/(2*num_units_in_tick), а не по ffprobe r_frame_rate одиночного IDR.
|
||||
Образцы не регенерировались. Runner /tmp/call-video-doc-audit-fixtures.py,
|
||||
данные/лог /tmp/call-video-doc-audit-fixtures.{json,log}.
|
||||
|
||||
Снимок17 заголовков до правок /tmp/call-video-doc-audit-before.json.
|
||||
Токены C/C++ до/после совпадают17/17; объявления/константы/структуры сохранены.
|
||||
Общий include всех10 C-заголовков проходит C99, всех17 — C++20/Qt6,
|
||||
-Wall -Wextra -Werror -fsyntax-only; /tmp/call-video-doc-audit-headers.{c,cpp},
|
||||
/tmp/call-video-doc-audit-validation.log. Git diff --check PASS.
|
||||
|
||||
В чек-листе закрыты10 src/call/call_video*.h,7 desktop call_video*.h и doc/call_video.txt;
|
||||
прочие статусы сохранены, src/call/call_priv.h и общий аудит call/архитектуры остаются частичными.
|
||||
Это завершение сверки перечисленных контрактов с кодом, не новая аппаратная приёмка видеозвонка.
|
||||
Qt runtime/GPU/камеры, APK/JVM, Windows/FreeBSD/Android на устройствах, sanitizers
|
||||
и полный check.sh в этом проходе не запускались. Исторические замеры не перепроверялись.
|
||||
Runtime/wire/тесты не менялись; коммит и push не выполнялись.
|
||||
|
||||
23. Закрытие внутреннего call_priv.h — 26.10.2026
|
||||
|
||||
По уточнению пользователя закрыта оставшаяся частичная отметка внутреннего состояния.
|
||||
Причиной прежней [~] была незавершённая сверка video lifetime (раздел20); контракты
|
||||
видеоядра/порта уже проверены в разделе22. Повторно прочитаны call.c целиком,
|
||||
call_priv.h, места доступа video_transport/port, реестр ll_queue и штатные проверки admission.
|
||||
|
||||
В заголовке описаны единственный uasync-владелец, sessions с индексом call_id,
|
||||
заимствованный find (возвращает и ENDED), внешняя аллокация entry->data и запрет
|
||||
переноса raw session/ctx в workers/отложенные команды. Поле session.ll не используется
|
||||
для реестра; это отмечено явно, его layout не менялся. Started_tb — начало отсчёта60с,
|
||||
а не готовое значение deadline; PHONE_BUSY/continue не перезапускают попытку.
|
||||
|
||||
Отражены реальное значение ACTIVE без обязательной готовности CM, немедленная очистка
|
||||
audio TX/video при ENDED, ожидание END_ACK/таймаута с удержанием CM/записи,
|
||||
отложенное освобождение обычного remove и синхронное освобождение оставшегося реестра при destroy.
|
||||
Уточнены все таймеры, watcher пути и ACTIVE stats, аудио FIFO750/drop-head,
|
||||
sequence до выделения entry, одноразовый send waiter и интервал агрегатов ожидания.
|
||||
Counters: started считает только созданные исходящие, ended — переходы обеих ролей;
|
||||
TX/RX — audio frames, c_tx_dropped считает overflow, c_rx_dropped не ведётся.
|
||||
Last_media_tb обновляется на RX; watchdog управляется traffic_timer. Headless callback
|
||||
хранится в ctx, а audio_ops — отдельно у instance и переживает пересоздание ctx.
|
||||
Новые функциональные дефекты не подтверждены; изменены только комментарии и статус аудита.
|
||||
|
||||
Проверки: snapshot /tmp/call-priv-doc-audit-before.h; C/C++ токены совпадают,
|
||||
объявления/структуры/константы сохранены. Include заголовка C99 и C++20 с
|
||||
-Wall -Wextra -Werror -fsyntax-only PASS; /tmp/call-priv-doc-audit-validation.log.
|
||||
Штатные цели test_call_admission/test_call_video_port/test_call_video_flush собраны;
|
||||
новые прогоны3/3 PASS. Admission:547 checks,BUSY22,no_event18,allocations0->0,
|
||||
проверены сохранение сессии/CM/таймеров при отказе, ACTIVE после маршрута,
|
||||
ENDED и потерянный END_ACK. Первый запуск sandbox заблокировал сокеты,
|
||||
успешный admission выполнен с разрешением вне sandbox.
|
||||
Логи /tmp/call-priv-doc-audit-{build,admission,test_call_video_port,test_call_video_flush}.log.
|
||||
Git diff --check PASS; предыдущие изменения video/doc сохранены.
|
||||
|
||||
Call_priv.h [~]→[V], остальные отметки сохранены. Все21 заголовок src/call теперь[V];
|
||||
doc/chat_call_arch.md и общий аудит call/платформ остаются[~]. Текущий статус архитектурного
|
||||
документа уточнён; исторические результаты разделов20–22 сохранены.
|
||||
Новые fullcheck/sanitizers/APK/Qt runtime/аппаратный звонок не выполнялись.
|
||||
Коммит/push не выполнялись.
|
||||
+83
-11
@@ -9,6 +9,9 @@ HEVC Main с Main/High tier (High начиная с level 4), 8-bit 4:2:0, progr
|
||||
HRD при наличии, геометрию и независимый IDR. Фактический level проверяется после
|
||||
открытия encoder. CAPS — 64 байта, header — 56 байт; старый формат не поддерживается.
|
||||
VBV является локальной настройкой software encoder, в сетевом контракте его нет.
|
||||
Проверка KEY в call_video.c ограничена envelope; разбор нужных полей VPS/SPS/PPS/IDR
|
||||
выполняет call_video_bitstream. Он не декодирует entropy payload и не заменяет decoder.
|
||||
У HEVC High tier также действуют консервативные Main-tier бюджеты HRD/CPB.
|
||||
Результат запуска decoder (generation + accepted) передаётся отдельно от router ACK:
|
||||
неподдержанный формат вызывает fallback в Auto, потеря references — запрос KEY.
|
||||
Камера, разрешение, частота до 60 fps, кодек и диапазон битрейта меняются во время звонка.
|
||||
@@ -22,7 +25,8 @@ SoundTouch и очередь устройства. Алгоритм регули
|
||||
Показ имеет собственное абсолютное расписание с плавным темпом по суммарному запасу до 1.6x;
|
||||
audio clock ограничивает опережение videoPTS-audioPTS до +40мс. Устойчивый долг при ограничении
|
||||
частоты экрана снимается пропуском только показа уже декодированных кадров.
|
||||
Подробности и границы измерения — в разделе «Независимое расписание показа» ниже.
|
||||
Подробности — в разделах «Часы показа и сквозная очередь», «Независимый desktop-планировщик»
|
||||
и «До 60 fps и восстановление presentation»; раннее «Независимое расписание показа» историческое.
|
||||
Искусственная тишина/тоны не продвигают исходную позицию. Формат видео меняется по PTS;
|
||||
новое поколение не уничтожает старые кадры, соответствующие ещё не прозвучавшей речи.
|
||||
|
||||
@@ -43,8 +47,69 @@ audio clock ограничивает опережение videoPTS-audioPTS до
|
||||
Состояние звонка/регулятора/роутера — uasync. Камера и encoder/decoder — платформенные
|
||||
workers. Ограниченный mailbox, одно ожидающее уведомление. Команды bitrate имеют
|
||||
call_id, video_generation и request_id; одно исполнение и последнее желаемое значение.
|
||||
Принятая настройка не заменяет измерение фактического выхода. Shutdown отменяет
|
||||
уведомления/таймеры, останавливает producers, затем освобождает очереди и codecs.
|
||||
Принятая настройка не заменяет измерение фактического выхода. Остановка освобождает
|
||||
уведомления/таймеры, очереди и codecs в порядке владельцев: desktop присоединяет workers
|
||||
перед close port; Android ставит stopped, присоединяет RX, закрывает port, затем TX handler
|
||||
закрывает камеру/encoder и завершает свой looper. Это разные контракты завершения.
|
||||
|
||||
Границы модулей и wire (сверено с кодом 26.10.2026)
|
||||
call_video.h: чистые проверки/сериализация доступны workers, receiver имеет одного владельца.
|
||||
Limits — независимые границы, полностью нулевой limit означает отсутствие
|
||||
проверенного профиля. Format: ненулевой generation, чётные размеры 16..4096, 1..60 fps,
|
||||
rotation=0/90/180/270, target ceiling 300000..4000000 бит/с, один profile=1/2.
|
||||
Поля формата, включая maximum_bps, неизменны внутри generation; текущий bitrate — отдельно.
|
||||
frame_id и capture PTS строго возрастают сквозь поколения; пропуск frame_id требует KEY.
|
||||
|
||||
CAPS: четыре limits по 16 байт: encode AVC, encode HEVC, decode AVC, decode HEVC.
|
||||
Внутри limit: width:2, height:2, fps:2, pixels_per_second:4, maximum_bps:4, level:1, profiles:1.
|
||||
Header фрагмента (смещения в байтах):
|
||||
0 call_id:8 8 generation:8 16 frame_id:8 24 capture_pts_us:8
|
||||
32 width:2 34 height:2 36 fps:2 38 rotation:2
|
||||
40 codec:1 41 flags:1 42 level:1 43 profile:1
|
||||
44 frame_size:4 48 offset:4 52 maximum_bps:4
|
||||
Все многобайтовые поля CAPS/header — BE. Длина payload 1..1200, полный кадр <=2 МиБ.
|
||||
Parse возвращает view внутри входного пакета; fragment.data уже указывает на payload
|
||||
данного offset. Локальный received_us в wire не входит. Control имеет отдельный формат:
|
||||
type:1 + call_id:8 + revision/generation:8, для CAPS ещё 64 байта; оба uint64 — native endian,
|
||||
как в текущей сигнализации call. Это описание действующего кода, не обещание cross-endian совместимости.
|
||||
|
||||
call_video_receiver хранит одну частичную сборку. Offset идёт непрерывно; повтор/перестановка,
|
||||
разрыв frame_id, регресс PTS/поколения и смена формата без нового generation нарушают цепочку.
|
||||
Возраст сборки >2с проверяется на следующем push; отдельного таймера сборки нет.
|
||||
Готовый буфер заимствован до следующего push/reset/destroy; очередь готовых кадров — у владельца.
|
||||
|
||||
call_video_transport принадлежит uasync: control/audio/video используют отдельные router services,
|
||||
control и video требуют подпись и шифрование. CAPS повторяются каждые 500мс до прикладного ACK.
|
||||
Configure может применить локальное состояние и затем вернуть -1 при отказе отправки CAPS.
|
||||
TX FIFO: 32 кадра, размах PTS <=500мс, суммарно <=2 МиБ; общий admission учитывает router debt.
|
||||
Голова ждёт audio_end_pts и CAPS_ACK. Flush выдаёт до 16 фрагментов, продолжает через call_soon;
|
||||
send_q/full debt ждут send_ready/audio/tick. Возраст головы >2с сбрасывает зависимую цепочку.
|
||||
Tick 50мс обновляет регулятор; timeout encoder-команды 12с выключает admission до configure_rate.
|
||||
Callback заимствует payload до возврата и не вызывает изменяющий call API реентерабельно.
|
||||
|
||||
call_video_port — единственный процессный mutex/mailbox. Attach/detach — uasync, workers используют
|
||||
потокобезопасные команды. Open того же id сохраняет RX/TX/peer/wake/received counter;
|
||||
другой id очищает их. Open/format/applied/range/decoded подтверждают приём команды,
|
||||
проверка активной сессии и исполнение происходят позже в uasync.
|
||||
Send=1 означает копирование пачки в mailbox, transport может позже отвергнуть её с key_serial.
|
||||
Send=0 не принимает ничего; transport busy сохраняет непринятый хвост без нового KEY.
|
||||
TX mailbox: до 8 кадров по 2 МиБ (до 16 МиБ); RX: до 8 кадров/4 МиБ.
|
||||
Pull передаёт владение data, освобождение u_free; RX wake вызывается под port mutex, без входа в port API.
|
||||
До detach останавливают пользователей и снимают transport callbacks через close с обработкой команды
|
||||
либо завершение call session. Сам detach не заменяет остановку транспорта.
|
||||
|
||||
call_video_rate — чистая модель 500мс окон с 50мс шагом; единицы — уникальные encoded bytes,
|
||||
без fragment headers. Invalid sample не меняет состояние/результат. Чистый step допускает до
|
||||
100 Мбит/с; диапазон звонка 300..4000 кбит/с ограничивают parser и transport.
|
||||
call_video_playout не владеет изображениями: WAIT сохраняет голову, SHOW резервирует до presented,
|
||||
DROP_ORDER/DROP_LATE освобождают только decoded presentation силами владельца.
|
||||
Desktop считает общий запас как received-completed-discarded без mutex порта;
|
||||
Android складывает снимок port.rx_frames с локальными pending/inputs/ready/awaiting.
|
||||
|
||||
Desktop смена пользовательского режима отменяет ещё не переданную worker-пачку и заменяет encoder;
|
||||
это отличается от смены bitrate с полным drain старого encoder. Android при смене геометрии
|
||||
сначала drain, отправляет pendingFrames и ждёт tx_idle перед format. Готовые RX-изображения
|
||||
старого поколения сохраняются до своего показа на обеих платформах.
|
||||
|
||||
Применение bitrate на ключевом кадре (уточнение пользователя)
|
||||
Worker хранит pending-команду и применяет её перед кодированием ближайшего
|
||||
@@ -99,8 +164,11 @@ GOP продолжается. Android сначала меняет bitrate чер
|
||||
Видео следует аудио при догоне, underrun, смене устройства и формате видеотрека.
|
||||
Завершение звонка безопасно во время переключения камеры, bitrate и decoder.
|
||||
|
||||
Далее журнал этапов разработки. Старые описания wire/profile/VBV ниже исторические;
|
||||
актуальный контракт приведён в начале, последние проверки — в конце документа.
|
||||
Далее журнал этапов разработки. Формулировки «текущий», «действующий», «пока» и результаты
|
||||
проверок относятся к дате соответствующего этапа; старые wire/profile/VBV/буферы/алгоритмы
|
||||
не задают нынешний контракт. Актуальная сводка приведена выше, подробности поздних этапов
|
||||
уточняют расписание, High tier, 60 fps и прогрев. Исторические аппаратные результаты и логи
|
||||
при сверке документации 26.10.2026 заново не воспроизводились.
|
||||
|
||||
Проверка SoundTouch, 14.10.2026
|
||||
test_soundtouch_clock сравнивает положение реальных четырёхсэмпловых фрагментов
|
||||
@@ -922,7 +990,7 @@ Windows-диагностика: AudioDiag теперь работает чере
|
||||
video late drop содержит точные PTS/AV/возраст первого позднего кадра за окно, playout —
|
||||
диапазоны всех late кадров. Измерения не корректируют Windows callback-estimate аудиочасы.
|
||||
|
||||
Восстановление без пропусков, 19.10.2026 — действующее поведение
|
||||
Восстановление без пропусков, 19.10.2026 — базовый этап; уточнения 19–20.10 ниже
|
||||
Encoded-кадр после принятия сохраняет ID/PTS и владельца до передачи дальше. TX port
|
||||
удерживает непринятый хвост пачки и повторяет его при уведомлении writable. Новая пачка
|
||||
не принимается во время обработки старой. Busy=0 не меняет references и не запрашивает KEY;
|
||||
@@ -949,7 +1017,8 @@ Decoder получает кадры строго последовательно,
|
||||
входы decoder ограничены отдельно восемью, чтобы output lookahead не блокировал запуск GPU.
|
||||
Основной backlog хранится encoded, а не большим RGB FIFO или удержанием всех аппаратных outputs.
|
||||
|
||||
Playout больше не содержит DROP_LATE/DROP_SCHEDULE. Invalid/order остаётся диагностируемой ошибкой.
|
||||
На этом этапе playout не содержал DROP_LATE/DROP_SCHEDULE; 20.10 возвращён только DROP_LATE
|
||||
для устойчивого долга при ограничении частоты экрана. Invalid/order остаётся диагностируемой ошибкой.
|
||||
CallTimeline публикует rate_ppm из фактического SourceSpan SoundTouch -> PCM на текущей позиции устройства.
|
||||
Первоначальный вариант управлял темпом по audioPTS-framePTS. Его заменила описанная ниже схема
|
||||
с подтверждённым временем показа, общей глубиной конвейера и небольшим рабочим запасом.
|
||||
@@ -1004,8 +1073,9 @@ N = полные encoded в port + pending input worker + принятые decod
|
||||
+ decoded FIFO + один выбранный, но ещё не подтверждённый GUI/Surface кадр.
|
||||
Перемещение между стадиями не меняет N; исключение кадра происходит после подтверждения показа
|
||||
или решения DROP_LATE для уже декодированного кадра (дополнение 20.10.2026 ниже).
|
||||
Снимок port.rx_frames берётся под mutex после локальных перемещений RX worker. Сетевая частичная
|
||||
сборка не считается полным кадром. Очереди отправителя не видны этому счётчику.
|
||||
На Android снимок port.rx_frames берётся под mutex после локальных перемещений RX worker.
|
||||
Desktop использует атомарный cumulative received-completed-discarded (см. независимый планировщик ниже).
|
||||
Сетевая частичная сборка не считается полным кадром. Очереди отправителя не видны этому счётчику.
|
||||
RX admission возобновляется при появлении места, без искусственного ожидания полного drain FIFO.
|
||||
Уведомление port будит desktop RX немедленно; callback снимается до освобождения владельца.
|
||||
|
||||
@@ -1018,7 +1088,8 @@ submit/receive независимы: EAGAIN сохраняет непринят
|
||||
SHOW резервирует кадр, presented(PTS, MONOTONIC) подтверждает его потребление и продвигает часы.
|
||||
Desktop ACK приходит после paintEvent; Android — после SurfaceTexture/RenderThread commit, не по OnFrameRendered.
|
||||
Это подтверждения платформенного потребления, а не измерение физического scanout экрана.
|
||||
Срок следующей головы фиксируется один раз: последнее ACK + период format / speed - оценка dispatch-to-ACK.
|
||||
Срок следующей головы фиксируется один раз: фаза последнего показа + период format / speed
|
||||
минус оценка dispatch-to-ACK. Фаза плавно корректируется по ACK; большая пауза привязывает её к ACK.
|
||||
Оценка lead сглаживается и принимает обычный ACK за целый цикл экрана; выбросы больше 1.25 периода +1мс игнорируются.
|
||||
Пустая очередь сохраняет привязку: готовый через 12мс следующий кадр не показывается раньше своего обычного срока.
|
||||
Поздно пришедшая голова выходит сразу после готовности; разрыв capture PTS не повторяется ожиданием.
|
||||
@@ -1031,7 +1102,8 @@ Desktop ACK приходит после paintEvent; Android — после Surfa
|
||||
Опережение аудио по-прежнему ограничено 40мс; невалидные аудиочасы запрещают новый показ без очистки FIFO.
|
||||
|
||||
Windows ожидает абсолютный срок через high-resolution waitable timer + event, без spin/timeBeginPeriod.
|
||||
Отказ high-resolution API явно логируется, остаётся regular timer; полный отказ handles использует condition_variable.
|
||||
Отказ high-resolution API явно логируется, остаётся regular timer; при отсутствии необходимых handles
|
||||
desktop start отклоняет запуск видео. Runtime ошибка ожидания передаётся RX для лога и остановки workers.
|
||||
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.
|
||||
|
||||
@@ -65,7 +65,12 @@ Android timing/trace/buffer/recovery26/26, voice/desktop30/30 и assembleDebug
|
||||
Следующая сверка закрыла публичные аудиоконтракты call.h/call_audio.h/call_headless.h/call_proto.h
|
||||
и desktop call_audio_engine.h. Уточнены сроки жизни callback/аргументов, фиксированный wire-формат,
|
||||
завершение и восстановление устройств. Изменения только в комментариях/doc; voice38/38 и четыре
|
||||
набора сигналинга/headless PASS. Подробности — раздел20 отчёта; call_priv и видеоконтракты пока частичны.
|
||||
набора сигналинга/headless PASS. Подробности — раздел20 отчёта.
|
||||
|
||||
Сверка видеоконтрактов и внутреннего `call_priv.h` завершена: все21 заголовок `src/call/` закрыты,
|
||||
также проверены17 C/C++ видеозаголовков и `doc/call_video.txt` (разделы22–23 отчёта).
|
||||
Уточнены владение сессией/видеотреком, асинхронное завершение и смысл счётчиков; реализации не менялись.
|
||||
Общий аудит архитектуры и платформенных разделов остаётся частичным, новая аппаратная приёмка не выполнялась.
|
||||
|
||||
## 1. Область
|
||||
|
||||
|
||||
+39
-27
@@ -1,42 +1,54 @@
|
||||
/* Внутреннее состояние call.c/call_video_transport.c; доступ только из uasync. */
|
||||
/* Внутренний реестр звонков: call.c владеет ctx/сессиями, video_transport и process
|
||||
* видеопорта обращаются к ним только из uasync. Workers используют публичный port API.
|
||||
* Сессия размещена в data внешнего ll_entry; sessions индексирует call_id, а не пару group/peer.
|
||||
* Find возвращает заимствованный указатель, в том числе на ENDED до удаления записи.
|
||||
* При ENDED аудио-TX/video останавливаются сразу; CM/запись могут ждать END_ACK/таймаут.
|
||||
* Обычное удаление снимает запись из реестра и освобождает entry через call_soon;
|
||||
* call_destroy отменяет callbacks и освобождает оставшиеся в реестре записи синхронно.
|
||||
* Не переносить указатели ctx/session в другой поток или отложенную команду: передавать call_id
|
||||
* и повторять find в uasync. Описание публичных действий/событий — call.h/call_proto.h. */
|
||||
#ifndef CALL_PRIV_H
|
||||
#define CALL_PRIV_H
|
||||
#include "call.h"
|
||||
#include "../../lib/ll_queue.h"
|
||||
|
||||
/* IDLE — только создание; RINGING бывает у обеих ролей, WAIT_DECISION — ожидание force
|
||||
* при телефонной занятости; ENDED в реестре уже не занимает линию и не принимает медиа. */
|
||||
enum call_state { CALL_ST_IDLE, CALL_ST_OUTGOING, CALL_ST_RINGING, CALL_ST_ACTIVE, CALL_ST_ENDED, CALL_ST_WAIT_DECISION };
|
||||
enum call_role { CALL_ROLE_CALLER, CALL_ROLE_CALLEE };
|
||||
struct call_video_transport;
|
||||
struct call_session {
|
||||
struct ll_entry ll;
|
||||
uint64_t call_id, group_id, peer_node_id;
|
||||
uint8_t role, state, reason;
|
||||
struct ll_entry ll; /* Не используется; реальный queue entry находится перед s == entry->data. */
|
||||
uint64_t call_id, group_id, peer_node_id; /* Неизменные ID попытки, группы маршрутизации и собеседника. */
|
||||
uint8_t role, state, reason; /* call_role/call_state; reason — CALL_REASON_* после перехода в ENDED. */
|
||||
uint8_t force; /* вызывающий подтвердил обход телефонной занятости */
|
||||
uint64_t started_tb; /* неизменный deadline попытки, включая выбор продолжения */
|
||||
uint8_t end_subcmd;
|
||||
uint64_t end_started_tb;
|
||||
struct call_ctx *ctx;
|
||||
struct CONN_MGR_HANDLE *cm_handle;
|
||||
void *ring_timer, *invite_timer;
|
||||
uint8_t no_route_strikes;
|
||||
void *traffic_timer, *teardown_timer, *stats_timer;
|
||||
uint8_t connection_reported, connection_type;
|
||||
uint64_t connection_next_hop, last_media_tb;
|
||||
uint16_t tx_media_seq;
|
||||
struct ll_queue *tx_q;
|
||||
struct queue_waiter_handle send_waiter;
|
||||
uint32_t c_tx_frames, c_rx_frames, c_tx_dropped, c_rx_dropped;
|
||||
uint64_t tx_wait_total_ms;
|
||||
uint32_t tx_wait_frames, tx_wait_max_ms;
|
||||
struct call_video_transport *video;
|
||||
uint64_t started_tb; /* Начало попытки get_time_tb (100мкс); 60с отсчитываются отсюда, включая WAIT_DECISION. */
|
||||
uint8_t end_subcmd; /* Финальная команда для повтора; 0 — нет своей команды либо уже принят END_ACK. */
|
||||
uint64_t end_started_tb; /* Начало ENDED в tb; предел повтора своей команды — 20с. */
|
||||
struct call_ctx *ctx; /* Заимствованный владелец, живёт до удаления сессии из реестра. */
|
||||
struct CONN_MGR_HANDLE *cm_handle; /* Собственный запрос оптимизации пути; ACTIVE не требует готового CM. */
|
||||
void *ring_timer, *invite_timer; /* Caller: 60с/повтор INVITE 300мс; callee: ring_timer — watchdog INVITE до 2с. */
|
||||
uint8_t no_route_strikes; /* Последовательные INVITE ticks без маршрута; сбрасывается при наличии маршрута. */
|
||||
void *traffic_timer, *teardown_timer, *stats_timer; /* RX watchdog 20с, завершение/END retry, путь и ACTIVE-статистика 1с. */
|
||||
uint8_t connection_reported, connection_type; /* Было событие CONNECTION / последний router path.type (0..3). */
|
||||
uint64_t connection_next_hop, last_media_tb; /* Кэш next hop и время последнего RX; watchdog использует traffic_timer. */
|
||||
uint16_t tx_media_seq; /* Следующий sequence аудиопакета с wrap uint16; расходуется до выделения TX entry. */
|
||||
struct ll_queue *tx_q; /* Собственная FIFO готовых audio packets [svc][media]; до 750, переполнение удаляет голову. */
|
||||
struct queue_waiter_handle send_waiter; /* Отменяемое одноразовое ожидание места в router audio send_q. */
|
||||
uint32_t c_tx_frames, c_rx_frames, c_tx_dropped, c_rx_dropped; /* TX принят router / валидный RX / TX overflow / RX не ведётся. */
|
||||
uint64_t tx_wait_total_ms; /* Сумма времени audio enqueue->drain, включая отказ router; сброс при отчёте stats. */
|
||||
uint32_t tx_wait_frames, tx_wait_max_ms; /* Число drained кадров/максимальное ожидание за тот же отчётный интервал. */
|
||||
struct call_video_transport *video; /* Собственное ленивое состояние видеотрека; cleanup обнуляет и освобождает его. */
|
||||
};
|
||||
struct call_ctx {
|
||||
struct UTUN_INSTANCE *inst;
|
||||
int initialized;
|
||||
struct ll_queue *sessions;
|
||||
uint32_t c_calls_started, c_calls_ended;
|
||||
call_media_recv_fn media_recv_fn;
|
||||
void *media_recv_arg;
|
||||
struct UTUN_INSTANCE *inst; /* Заимствованное ядро, владеющее ctx в inst->call; audio_ops хранится отдельно у ядра. */
|
||||
int initialized; /* Сигналинг привязан; 1 также во время регистрации media services/отката call_init. */
|
||||
struct ll_queue *sessions; /* Собственные queue entries с call_session в data и 8-байтовым индексом call_id. */
|
||||
uint32_t c_calls_started, c_calls_ended; /* Созданные исходящие / все переходы в ENDED; это не парные счётчики. */
|
||||
call_media_recv_fn media_recv_fn; /* Один необязательный headless RX callback, вызываемый в uasync. */
|
||||
void *media_recv_arg; /* Заимствованный arg до замены callback/destroy; пересоздание ctx сбрасывает привязку. */
|
||||
};
|
||||
/* Найти call_id, включая ENDED: заимствованная сессия либо NULL (также при NULL ctx/sessions).
|
||||
* Только uasync; не проверяет state/group/peer. После удаления/destroy указатель использовать нельзя. */
|
||||
struct call_session *call_session_find(struct call_ctx *ctx, uint64_t call_id);
|
||||
#endif
|
||||
+36
-18
@@ -1,7 +1,10 @@
|
||||
/* Общий контракт видеотрека. Все операции выполняет владелец в uasync-потоке.
|
||||
/* Общий контракт видеотрека: чистые проверки/сериализация доступны любому потоку,
|
||||
* один receiver принадлежит одному владельцу, без внутренней синхронизации.
|
||||
* Сетевой transport использует receiver из uasync; codec workers используют проверки формата.
|
||||
* Кодеки: H.264 Baseline/Constrained Baseline, H.265 Main (Main/High tier), 8 bit,
|
||||
* 4:2:0, progressive, без B-кадров/reorder. DPB ограничен уровнем потока.
|
||||
* Поколение меняется только при смене формата/камеры; bitrate не меняет поколение.
|
||||
* Поля format неизменны внутри generation; его увеличивают при смене формата/камеры.
|
||||
* Текущий bitrate encoder не входит в format и не меняет generation.
|
||||
* KEY: config_length:4BE + Annex B configuration + Annex B access unit (как uAV2).
|
||||
* Приём хранит один частичный кадр. Готовый view заимствован до следующего push/reset.
|
||||
* Очередью готовых кадров, decoder и показом по audio clock владеет вызывающий. */
|
||||
@@ -31,29 +34,31 @@ extern "C" {
|
||||
* Поля — независимые верхние границы, не обещание поддержки их сочетания:
|
||||
* выбранный режим дополнительно проверяется открытием платформенного codec. */
|
||||
struct call_video_limit {
|
||||
uint16_t width, height, fps;
|
||||
uint32_t pixels_per_second, maximum_bps;
|
||||
uint16_t width, height, fps; // Верхние границы осей (16..4096) и частоты (1..60); нулевой профиль — все поля нулевые.
|
||||
uint32_t pixels_per_second, maximum_bps; // Ограничение width*height*fps и целевого bitrate (300000..4000000 бит/с).
|
||||
uint8_t profiles; // Маска режимов: AVC Baseline/CB; HEVC Main с Main/High tier. Приём каждого проверяется отдельно.
|
||||
uint8_t level;
|
||||
};
|
||||
struct call_video_caps { struct call_video_limit encode[2], decode[2]; };
|
||||
struct call_video_format {
|
||||
uint64_t generation;
|
||||
uint16_t width, height, fps, rotation;
|
||||
uint8_t codec, level;
|
||||
uint64_t generation; // Ненулевой, монотонно возрастающий номер формата в одном направлении звонка.
|
||||
uint16_t width, height, fps, rotation; // Чётные размеры 16..4096, 1..60 fps, поворот отображения 0/90/180/270 градусов.
|
||||
uint8_t codec, level; // CALL_VIDEO_H264/H265 и AVC level_idc / HEVC general_level_idc.
|
||||
uint32_t maximum_bps; // Предел целевого bitrate; не обещание мгновенного размера выхода/CPB.
|
||||
uint8_t profile;
|
||||
uint8_t profile; // Один режим 1/2, не маска: AVC Baseline/CB либо HEVC Main с Main/High tier.
|
||||
};
|
||||
struct call_video_frame {
|
||||
uint64_t call_id, frame_id;
|
||||
int64_t pts_us;
|
||||
uint64_t call_id, frame_id; // Ненулевые ID; frame_id возрастает сквозь поколения, пропуск требует KEY.
|
||||
int64_t pts_us; // Capture MONOTONIC отправителя: 0..INT64_MAX/2, строго возрастает сквозь поколения.
|
||||
struct call_video_format format;
|
||||
uint32_t size, offset, length;
|
||||
uint8_t flags;
|
||||
const uint8_t *data;
|
||||
uint32_t size, offset, length; // Размер всего кадра, смещение и размер данного фрагмента; полный кадр: offset=0, length=size.
|
||||
uint8_t flags; // Только CALL_VIDEO_KEY либо 0.
|
||||
const uint8_t *data; // Начало payload данного фрагмента, уже со смещением offset; владение задаёт вызывающий API.
|
||||
int64_t received_us; // Только локальная RX port метка MONOTONIC; по сети не передаётся.
|
||||
};
|
||||
|
||||
/* 1 — все limits структурно корректны, включая полностью нулевые CAPS; 0 — ошибка/NULL.
|
||||
* Не проверяет реальные codec и совместимость сочетания независимых границ. */
|
||||
int call_video_caps_valid(const struct call_video_caps *caps);
|
||||
/* После успешных платформенных проб width×height и height×width расширяет обе осевые
|
||||
* границы limit до длинной стороны, сохраняя pixel-rate/FPS/bitrate/profile/level.
|
||||
@@ -61,34 +66,47 @@ int call_video_caps_valid(const struct call_video_caps *caps);
|
||||
* 0 — успех, -1 — неверный limit/codec (limit не меняется). */
|
||||
int call_video_limit_orientations(struct call_video_limit *limit, int codec);
|
||||
/* format задаёт generation/размер/FPS/rotation/maximum_bps, level=0, profile=0 для auto.
|
||||
* codec=0: H.265, затем H.264. Успех: codec (1/2), заполняются codec и минимальный level.
|
||||
* codec=0: H.265, затем H.264. Успех: codec (1/2), заполняются codec/profile и минимальный level.
|
||||
* 0 — нет совместимого режима, -1 — неверные аргументы; на отказе format не меняется. */
|
||||
int call_video_choose(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
||||
struct call_video_format *format);
|
||||
/* Проверка уже выбранного/полученного режима, включая оба направленных CAPS и level. */
|
||||
/* 1 — формат допустим для sender.encode и receiver.decode, 0 — нет/неверный аргумент. */
|
||||
int call_video_format_supported(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
||||
const struct call_video_format *format);
|
||||
/* 1/0: проверка полей и таблиц level по padded geometry/FPS/bitrate, без открытия codec. */
|
||||
int call_video_format_valid(const struct call_video_format *format);
|
||||
/* Нормативный предел DPB в кадрах по coded geometry и level; 0 при неверном уровне. */
|
||||
unsigned call_video_dpb_limit(int codec, unsigned level, unsigned width, unsigned height);
|
||||
/* 1/0: ненулевые HRD bitrate (бит/с) и cpb (биты) в пределах таблицы уровня.
|
||||
* nal!=0 включает NAL-множитель; для HEVC используется консервативная таблица Main tier. */
|
||||
int call_video_hrd_valid(int codec, unsigned level, int nal, uint64_t bitrate, uint64_t cpb);
|
||||
/* Сравнение всех полей format, включая generation; оба указателя обязательны. */
|
||||
int call_video_same_format(const struct call_video_format *a, const struct call_video_format *b);
|
||||
/* CAPS: четыре 16-байтовых limits (encode AVC/HEVC, decode AVC/HEVC), integers BE.
|
||||
* 0/-1; при ошибке выход не меняется. Parse требует ровно CALL_VIDEO_CAPS_BYTES. */
|
||||
int call_video_caps_encode(const struct call_video_caps *caps, uint8_t out[CALL_VIDEO_CAPS_BYTES]);
|
||||
int call_video_caps_parse(const uint8_t *data, size_t length, struct call_video_caps *out);
|
||||
/* Весь header проверяется до записи/публикации view. Отказ: -1, выход не меняется. */
|
||||
/* Header 56 байт BE + 1..1200 байт payload; size всего кадра <=2 МиБ.
|
||||
* Encode копирует fragment.data[0..length); parse возвращает view внутри data.
|
||||
* Успех 0; весь header проверяется до записи/публикации view. Отказ -1, выход не меняется. */
|
||||
int call_video_fragment_encode(const struct call_video_frame *frame, uint8_t *out, size_t capacity, size_t *length);
|
||||
int call_video_fragment_parse(const uint8_t *data, size_t length, struct call_video_frame *out);
|
||||
|
||||
struct call_video_receiver;
|
||||
/* Создать пустую сборку, изначально требующую KEY; NULL — ошибка выделения. */
|
||||
struct call_video_receiver *call_video_receiver_create(void);
|
||||
/* Destroy/reset допускают NULL. Reset освобождает буфер и забывает ID/PTS/формат. */
|
||||
void call_video_receiver_destroy(struct call_video_receiver *receiver);
|
||||
void call_video_receiver_reset(struct call_video_receiver *receiver);
|
||||
/* 1 — полный кадр, 0 — частичный/пропущенный, -1 — неверный поток; после пропуска нужен KEY.
|
||||
* bytes — строго один пакет, уже проверенный на принадлежность call_id/peer/group владельцем.
|
||||
* now_us — монотонное время для ограничения жизни частичной сборки (2с).
|
||||
* fragment — валидный заголовок и payload одного фрагмента, call_id/peer/group проверяет владелец.
|
||||
* now_us — неубывающее локальное время; возраст частичной сборки >2с проверяется при push.
|
||||
* Фрагменты одного кадра идут без разрывов offset; повтор/перестановка сбрасывают сборку.
|
||||
* KEY здесь проверяется только по envelope; NAL проверяет call_video_bitstream/платформа.
|
||||
* Новый формат принимается только с KEY. Старые готовые кадры вызывающий сохраняет до их PTS. */
|
||||
int call_video_receiver_push(struct call_video_receiver *receiver, const struct call_video_frame *fragment,
|
||||
uint64_t now_us, struct call_video_frame *complete);
|
||||
/* 1 — нужен независимый KEY (также для NULL), 0 — цепочка сборки непрерывна. */
|
||||
int call_video_receiver_needs_key(const struct call_video_receiver *receiver);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* progressive 8-bit 4:2:0, один spatial/temporal layer, без reorder. DPB — по Annex A.
|
||||
* HEVC допускает dependent segments внутри IDR и явный pic_output_flag=1;
|
||||
* первый слайс независимый, адреса остальных — в границах CTB геометрии SPS.
|
||||
* Результат публикуется целиком только после успешной проверки VPS/SPS/PPS и IDR.
|
||||
* Это проверка нужных полей parameter sets и заголовков IDR, без полного разбора entropy payload;
|
||||
* она не заменяет проверку реальным decoder. Результат публикуется только при успехе.
|
||||
* Все функции доступны worker-потокам; состояния и платформенных объектов нет. */
|
||||
#ifndef CALL_VIDEO_BITSTREAM_H
|
||||
#define CALL_VIDEO_BITSTREAM_H
|
||||
@@ -12,9 +13,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
struct call_video_bitstream {
|
||||
unsigned width, height, level, profile, dpb;
|
||||
unsigned width, height, level, profile, dpb; // Видимая геометрия после crop, wire level/profile и SPS DPB в кадрах.
|
||||
};
|
||||
/* codec=1/2, data — полный KEY envelope размером 9..CALL_VIDEO_MAX_FRAME.
|
||||
* 0 — заполнен out, -1 — отказ с логом; out на ошибке не меняется, data заимствован на время вызова. */
|
||||
int call_video_inspect_key(int codec, const uint8_t *data, size_t length, struct call_video_bitstream *out);
|
||||
/* Inspect + сравнение геометрии/profile и actual level <= format.level; 0/-1.
|
||||
* AVC CB принимается и в Baseline envelope. Сам format предварительно валидирует владелец. */
|
||||
int call_video_check_key(const struct call_video_format *format, const uint8_t *data, size_t length);
|
||||
/* KEY-проба 1280x720 либо 1920x1080, включая обратную ориентацию, fps=30 либо 60.
|
||||
* Буфер 8192 байт достаточен; возвращает размер или -1 с диагностикой. */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* Общий таймер показа видео, один владелец — RX worker. Изображениями не владеет.
|
||||
/* Общий таймер показа видео, один владелец — worker показа. Изображениями не владеет.
|
||||
* poll вызывается для первого готового decoded кадра. queued — все ещё не показанные полные кадры.
|
||||
* SHOW/DROP_ORDER/DROP_LATE потребляют decoded кадр, WAIT сохраняет его. Encoded входы не пропускаются.
|
||||
* DROP_ORDER/DROP_LATE освобождают decoded кадр, SHOW удерживает его до presented; WAIT сохраняет голову.
|
||||
* Сам модуль очереди не меняет. Encoded входы не пропускаются.
|
||||
* received_us ставится на входе RX port, до ожидания worker; все локальные времена — MONOTONIC, не BOOTTIME.
|
||||
* SHOW резервирует один кадр до presented(pts, фактическое время); только ACK продвигает часы.
|
||||
* Короткое опустошение сохраняет расписание. Темп плавно зависит от суммарного запаса кадров.
|
||||
@@ -14,7 +15,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
struct call_video_playout_frame {
|
||||
int64_t pts_us, received_us;
|
||||
int64_t pts_us, received_us; // Capture PTS отправителя и локальное время полной RX-сборки, мкс.
|
||||
uint32_t interval_us; // 1000000/format.fps; разрыв capture PTS не создаёт повторную паузу.
|
||||
};
|
||||
enum call_video_playout_action {
|
||||
@@ -22,9 +23,9 @@ enum call_video_playout_action {
|
||||
};
|
||||
struct call_video_playout_event;
|
||||
struct call_video_playout {
|
||||
uint64_t call_id;
|
||||
int anchored, audio_valid;
|
||||
int64_t last_pts, last_deadline, deadline_us;
|
||||
uint64_t call_id; // Для диагностики; не фильтрует входные кадры по звонку.
|
||||
int anchored, audio_valid; // Есть фаза подтверждённого показа / последний clock был valid.
|
||||
int64_t last_pts, last_deadline, deadline_us; // Последний выбранный/пропущенный PTS, фаза и срок следующего poll.
|
||||
int64_t report_us, last_show_us, interval_min, interval_max, av_min, av_max, dispatch_max, data_shift;
|
||||
int64_t last_show_pts, last_received, gap_pts, gap_received, gap_dispatch;
|
||||
uint32_t speed_ppm; // Темп аудио + коррекция отставания, максимум 1.6x.
|
||||
@@ -34,7 +35,7 @@ struct call_video_playout {
|
||||
unsigned source_gaps;
|
||||
int64_t source_gap_max; // Большие разрывы capture PTS, уже прошедшие до показа.
|
||||
struct call_video_playout_frame pending; // Единственный выбранный кадр до подтверждения потребителем.
|
||||
int awaiting;
|
||||
int awaiting; // Есть SHOW без соответствующего presented; новые SHOW запрещены.
|
||||
int64_t selected_us, pending_av, head_pts, head_due_us, lead_us, rate_us;
|
||||
int64_t head_show_us; // Плановая презентация головы; GUI ACK корректирует фазу плавно.
|
||||
unsigned queued, reserve; // reserve — будущие кадры сверх головы, включая необходимый pipeline backend.
|
||||
@@ -54,16 +55,20 @@ struct call_video_playout_event {
|
||||
/* Reporter вне timer: форматирует и выводит сохранённое событие через DEBUG категории call. */
|
||||
void call_video_playout_log_event(const struct call_video_playout_event *event);
|
||||
|
||||
/* Локальное MONOTONIC время, мкс (Windows использует get_time_us); не capture PTS удалённого узла. */
|
||||
int64_t call_video_playout_now_us(void);
|
||||
/* Обнулить состояние и logger, задать id. p обязателен; вызывать до первого poll/после остановки владельца. */
|
||||
void call_video_playout_init(struct call_video_playout *p, uint64_t call_id);
|
||||
/* frame==NULL обновляет состояние без снятия временной привязки.
|
||||
* deadline_us — абсолютный срок при WAIT, проверять аудиочасы не реже чем раз в 5мс.
|
||||
* queued учитывает encoded, decoder, decoded и GUI каждый ровно один раз; reserve обычно 1.
|
||||
* p обязателен, now_us — неубывающий MONOTONIC; NULL/invalid audio означает WAIT.
|
||||
* Некорректный PTS/received/interval даёт DROP_ORDER только при valid audio и отсутствии awaiting.
|
||||
* source_epoch не участвует: коррекция аудио не означает разрыв видеокодека. */
|
||||
enum call_video_playout_action call_video_playout_poll(struct call_video_playout *p,
|
||||
const struct call_video_playout_frame *frame, int64_t now_us, const struct call_clock_snapshot *audio,
|
||||
unsigned queued, unsigned reserve);
|
||||
/* Подтверждение выбранного PTS в MONOTONIC. Повторный/чужой ACK не меняет расписание. */
|
||||
/* Подтверждение выбранного PTS в MONOTONIC, не раньше выбора. Повторный/чужой ACK логируется и игнорируется. */
|
||||
void call_video_playout_presented(struct call_video_playout *p, int64_t pts_us, int64_t now_us);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* call_video_port — обмен готовыми видеокадрами между платформенными workers и ядром звонка.
|
||||
* Attach подключает ядро; для активного call_id: open → send/pull/status → close. Перед отправкой настроить encoder через format.
|
||||
* Attach/detach — uasync; остальные API потокобезопасны. До detach остановить пользователей порта.
|
||||
* Attach/detach — uasync; остальные API потокобезопасны. До detach остановить пользователей порта
|
||||
* и снять transport callbacks через close с обработкой команды либо остановку сессии звонка.
|
||||
* Send копирует кадры; pull передаёт буфер вызывающему, освобождение — u_free(). Команды исполняются в uasync.
|
||||
* В процессе один порт: TX до восьми кадров по 2 МиБ, RX до восьми кадров и 4 МиБ суммарно. */
|
||||
#ifndef CALL_VIDEO_PORT_H
|
||||
@@ -24,7 +25,7 @@ struct call_video_port_status {
|
||||
};
|
||||
// ── Привязка к ядру ──
|
||||
|
||||
// Подключить единственное ядро к порту; 0/-1.
|
||||
// Подключить единственное ядро к порту; 0/-1. Повторный attach, даже того же instance, — ошибка.
|
||||
int call_video_port_attach(struct UTUN_INSTANCE *inst);
|
||||
|
||||
// Отменить ожидающий post и освободить очереди; выполнять до уничтожения ядра.
|
||||
@@ -32,7 +33,8 @@ void call_video_port_detach(struct UTUN_INSTANCE *inst);
|
||||
|
||||
// ── Открытие и закрытие видеотрека ──
|
||||
|
||||
// Скопировать локальные capabilities и запланировать настройку видеотрека; 0/-1, не подтверждение готовности пира.
|
||||
// Скопировать CAPS и запланировать настройку; 0/-1. Active-call проверяется асинхронно.
|
||||
// Повтор open того же id сохраняет RX/TX/счётчики/wake; другой id очищает их.
|
||||
int call_video_port_open(uint64_t id, const struct call_video_caps *caps);
|
||||
|
||||
// Закрыть текущий id и очистить его очереди; чужой id игнорируется.
|
||||
@@ -54,7 +56,9 @@ int call_video_port_set_wake(uint64_t id, void (*wake)(void *), void *arg);
|
||||
|
||||
// ── Настройка кодера и исходящие кадры ──
|
||||
|
||||
// Передать фактический формат и битрейт открытого encoder; формат копируется. 0 — команда принята, -1 — отказ.
|
||||
// Передать формат/битрейт открытого encoder; 0 — команда принята, -1 — отказ.
|
||||
// Перед сменой формата владелец при необходимости ждёт tx_idle; сама команда не ждёт drain.
|
||||
// Итог проверять по status.error/configured_generation; 0 не подтверждает настройку транспорта.
|
||||
int call_video_port_format(uint64_t id, const struct call_video_format *format, uint32_t bps);
|
||||
|
||||
// Подтвердить команду status.rate_generation/rate_request: bps — применённый битрейт, 0 — отказ encoder. 0/-1.
|
||||
@@ -62,9 +66,12 @@ int call_video_port_applied(uint64_t id, uint64_t generation, uint64_t request,
|
||||
|
||||
#define CALL_VIDEO_PORT_TX_FRAMES 8u
|
||||
#define CALL_VIDEO_PORT_TX_BYTES (CALL_VIDEO_PORT_TX_FRAMES * CALL_VIDEO_MAX_FRAME)
|
||||
/* Атомарно копирует готовую пачку одного звонка. 1 — принята целиком, 0 — mailbox занят,
|
||||
/* Атомарно копирует готовую пачку одного звонка/формата с возрастающими frame_id/PTS.
|
||||
* 1 — принята целиком, 0 — mailbox занят,
|
||||
* -1 — ошибка; на 0/-1 ни один кадр не принят. count: 1..CALL_VIDEO_PORT_TX_FRAMES.
|
||||
* Входные кадры всегда остаются у вызывающего. При 0 повторить ту же пачку после освобождения порта. */
|
||||
* Входные кадры всегда остаются у вызывающего. При 0 повторить ту же пачку после освобождения порта.
|
||||
* 1 подтверждает копирование в mailbox; transport проверяет формат/цепочку позже.
|
||||
* Transport busy сохраняет непринятый хвост, ошибка освобождает его и увеличивает key_serial. */
|
||||
int call_video_port_send(const struct call_video_frame *frames, unsigned count);
|
||||
|
||||
// ── Входящие кадры ──
|
||||
|
||||
+14
-10
@@ -1,7 +1,9 @@
|
||||
/* Регулятор исходящего видеотрека: чистое состояние без таймеров, транспорта и encoder.
|
||||
* Владелец вызывает step каждые 50мс с согласованными окнами доставки/производства 500мс.
|
||||
* Все размеры — уникальные байты сжатого видео без заголовков. Held A/V не входит в debt.
|
||||
* После смены транспортной эпохи нужен init. Команды encoder сериализует вызывающий. */
|
||||
* После смены транспортной эпохи нужен init. Команды encoder сериализует вызывающий.
|
||||
* Step допускает ненулевые границы/applied до 100 Мбит/с; диапазон звонка
|
||||
* 300..4000 кбит/с проверяют parse_range и transport. */
|
||||
#ifndef CALL_VIDEO_RATE_H
|
||||
#define CALL_VIDEO_RATE_H
|
||||
#include <stdint.h>
|
||||
@@ -18,32 +20,34 @@ enum call_video_rate_reason {
|
||||
};
|
||||
|
||||
struct call_video_rate_sample {
|
||||
uint64_t now_us, window_end_us;
|
||||
uint64_t delivered_bytes, produced_bytes, debt_bytes;
|
||||
uint32_t applied_bps, minimum_bps, maximum_bps;
|
||||
uint32_t baseline_rtt_us, rtt_us, frame_interval_us;
|
||||
uint64_t now_us, window_end_us; // Неубывающее время и граница завершённого 50мс интервала: 0 <= now-end <50мс.
|
||||
uint64_t delivered_bytes, produced_bytes, debt_bytes; // ACK/выход за 500мс и текущий долг; каждый <=UINT32_MAX.
|
||||
uint32_t applied_bps, minimum_bps, maximum_bps; // Фактически применённый bitrate и желаемые границы, бит/с.
|
||||
uint32_t baseline_rtt_us, rtt_us, frame_interval_us; // RTT и период кадра 1..1000000мкс для допуска штатного inflight.
|
||||
uint64_t debt_limit_bytes; /* Жёсткий предел admission, независимо от ACK-оценки. */
|
||||
int valid, ready, no_route, receive_busy, probing, locally_held, command_pending;
|
||||
int valid, ready, no_route, receive_busy, probing, locally_held, command_pending; // Флаги окна, канала и encoder-команды.
|
||||
};
|
||||
|
||||
struct call_video_rate_result {
|
||||
uint32_t target_bps;
|
||||
enum call_video_rate_reason reason;
|
||||
uint32_t target_bps; // Рекомендация; при pending остаётся applied, даже если диапазон изменился.
|
||||
enum call_video_rate_reason reason; // Причина рекомендации либо удержания/блокировки.
|
||||
int admit; /* Уже принятые кадры продолжают drain; новые запрещены при переполнении. */
|
||||
uint64_t delivery_bps, excess_bytes;
|
||||
uint64_t delivery_bps, excess_bytes; // delivered_bytes*16 и долг сверх допуска RTT + два периода кадра.
|
||||
};
|
||||
|
||||
struct call_video_rate {
|
||||
// Внутренняя история; изменяется только init/step у одного владельца.
|
||||
uint64_t last_us, next_change_us, previous_debt, high_since_us, last_log_us;
|
||||
uint32_t applied_bps;
|
||||
int initialized, high;
|
||||
};
|
||||
|
||||
/* Сбросить историю к now_us; NULL диагностируется и игнорируется. */
|
||||
void call_video_rate_init(struct call_video_rate *rate, uint64_t now_us);
|
||||
/* Атомарная настройка min:max в десятичных кбит/с, 300 <= min <= max <= 4000.
|
||||
* На ошибке выходные значения не меняются; результат 0/-1. */
|
||||
int call_video_rate_parse_range(const char *text, uint32_t *minimum_bps, uint32_t *maximum_bps);
|
||||
/* 0 — результат получен, -1 — недопустимый снимок (состояние не меняется).
|
||||
/* 0 — результат получен, -1 — недопустимый снимок (состояние и result не меняются).
|
||||
* target != applied — рекомендация, не подтверждение применения. Pending запрещает новую команду. */
|
||||
int call_video_rate_step(struct call_video_rate *rate, const struct call_video_rate_sample *sample,
|
||||
struct call_video_rate_result *result);
|
||||
|
||||
@@ -11,7 +11,9 @@ struct call_session;
|
||||
extern "C" {
|
||||
#endif
|
||||
struct call_video_ops {
|
||||
// peer/frame заимствованы на время callback; frame — полный собранный encoded кадр.
|
||||
void (*capabilities)(void *arg, const struct call_video_caps *peer);
|
||||
// 1 — есть место для полного кадра bytes, 0 — router удерживает первый фрагмент/ACK до resume.
|
||||
int (*receive_ready)(void *arg, uint32_t bytes);
|
||||
void (*frame)(void *arg, const struct call_video_frame *frame);
|
||||
void (*need_key)(void *arg);
|
||||
@@ -19,8 +21,11 @@ struct call_video_ops {
|
||||
void (*decoded)(void *arg, uint64_t generation, int accepted);
|
||||
void (*writable)(void *arg, int admit); // Разрешение новых входов encoder; также будит retry сохранённой пачки.
|
||||
};
|
||||
/* Настройка активного звонка, включая receive-only. NULL ops снимает платформенные callbacks.
|
||||
* capabilities отражают только реально проверенные профили. 0 — успешно, -1 — ошибка. */
|
||||
/* Настройка активного звонка, включая receive-only. При ops обязательны frame/receive_ready;
|
||||
* NULL ops снимает callbacks, обнуляет локальные CAPS/регулятор и очищает TX/частичную сборку RX.
|
||||
* caps обязательны даже при NULL ops; отражают только реально проверенные профили.
|
||||
* 0 — CAPS поставлены в router, -1 — ошибка; при отказе отправки настройка уже может быть применена.
|
||||
* CAPS повторяются до прикладного CAPS_ACK; успешный configure ещё не разрешает TX. */
|
||||
int call_video_configure(struct UTUN_INSTANCE *inst, uint64_t call_id, const struct call_video_caps *caps,
|
||||
const struct call_video_ops *ops, void *arg);
|
||||
/* Копирует один полный кадр. 1 принят, 0 временная занятость, -1 неверный формат/сессия/цепочка.
|
||||
@@ -29,10 +34,12 @@ int call_video_configure(struct UTUN_INSTANCE *inst, uint64_t call_id, const str
|
||||
* Каждый кадр выходит только после соответствующего аудио; deadline головы 2с.
|
||||
* При истечении deadline сбрасывается вся зависимая цепочка и запрашивается KEY. */
|
||||
int call_video_send_frame(struct UTUN_INSTANCE *inst, const struct call_video_frame *frame);
|
||||
/* Повторить доставку router RX после освобождения места платформой; чужой/закрытый id игнорируется. */
|
||||
void call_video_resume_receive(struct UTUN_INSTANCE *inst, uint64_t call_id);
|
||||
/* Decoder потерял references. Запрос идемпотентный, отправляется не чаще раза в 500мс. */
|
||||
void call_video_request_key(struct UTUN_INSTANCE *inst, uint64_t call_id);
|
||||
/* Результат запуска decoder конкретного поколения, отдельно от потери references. */
|
||||
/* Результат последнего полностью полученного поколения, accepted=0/1, 0/-1.
|
||||
* Отправка control при необходимости повторяется timer, отдельно от потери references. */
|
||||
int call_video_decode_result(struct UTUN_INSTANCE *inst, uint64_t call_id, uint64_t generation, int accepted);
|
||||
/* После открытия encoder; format — согласованный и фактически проверенный режим.
|
||||
* maximum_bps — дополнительный предел регулятора, не выше format.maximum_bps;
|
||||
@@ -44,10 +51,13 @@ int call_video_decode_result(struct UTUN_INSTANCE *inst, uint64_t call_id, uint6
|
||||
* applied=0 в rate_applied означает отказ encoder, выключает регулятор до configure_rate. */
|
||||
int call_video_configure_rate(struct UTUN_INSTANCE *inst, uint64_t call_id, const struct call_video_format *format,
|
||||
uint32_t applied_bps, uint32_t maximum_bps);
|
||||
/* 0 — ненулевой результат принят; -1 — stale/неверный результат либо обработанный отказ applied_bps=0. */
|
||||
int call_video_rate_applied(struct UTUN_INSTANCE *inst, uint64_t call_id, uint64_t generation,
|
||||
uint64_t request, uint32_t applied_bps);
|
||||
|
||||
/* Только call.c: управление общей сессией и граница уже отправленного аудио. */
|
||||
/* Только call.c: bind 0/-1 регистрирует два router-сервиса; unbind снимает их.
|
||||
* Stop отменяет timer/продолжение/send waiter и освобождает transport сессии.
|
||||
* Audio_sent обновляет границу уже отправленного аудио и повторяет flush; end_pts_us<0 игнорируется. */
|
||||
int call_video_transport_bind(struct UTUN_INSTANCE *inst);
|
||||
void call_video_transport_unbind(struct UTUN_INSTANCE *inst);
|
||||
void call_video_transport_stop(struct call_session *session);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/* Decoder видеотрека: один RX worker владеет всем экземпляром, submit/receive не потокобезопасны.
|
||||
* Полный KEY проверяется общим C verifier; FFmpeg дополнительно проверяет PTS/profile/level/геометрию выхода.
|
||||
* Новый формат сначала завершает старый через EOS. Готовые изображения сохраняет владелец.
|
||||
* capabilities — отдельная блокирующая worker-проба с процессным кэшем, mutex и отменой. */
|
||||
#pragma once
|
||||
#include "../../../src/call/call_video.h"
|
||||
#include <QImage>
|
||||
@@ -12,7 +16,8 @@ public:
|
||||
explicit CallVideoDecoder(Backend backend = Backend::Automatic);
|
||||
~CallVideoDecoder();
|
||||
// 1 — принят/получен, 0 — занят/ещё не готов, -1 — ошибка с подробностями в логе.
|
||||
// При submit=0 владелец сохраняет исходный frame. receive отдаёт metadata без data.
|
||||
// При submit=0 владелец сохраняет исходный frame. При 1 encoded bytes скопированы в FFmpeg,
|
||||
// metadata сохранена до receive; очередь до восьми входов. receive отдаёт metadata без data и владеющий QImage.
|
||||
int submit(const call_video_frame& frame);
|
||||
int receive(call_video_frame& frame, QImage& image);
|
||||
int finish(); // EOS: 1 принят, 0 нужно сначала получить выход, -1 ошибка.
|
||||
@@ -21,8 +26,8 @@ public:
|
||||
bool hardware() const;
|
||||
QString error() const;
|
||||
void useSoftware(); // После реального отказа устройства: следующий KEY открывает CPU, без повторной GPU пробы.
|
||||
void close();
|
||||
// running отменяет длительную пробу при stop; незавершённый результат не кэшируется.
|
||||
void close(); // Отбрасывает pending inputs, освобождает FFmpeg/GPU; выбор backend и текст ошибки сохраняются.
|
||||
// running отменяет ожидание lock/пробу; завершённый кэш может вернуться сразу, его не очищает close().
|
||||
static call_video_caps capabilities(const std::atomic<bool>* running = nullptr);
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
@@ -17,8 +17,11 @@ struct CallVideoRateCompletion {
|
||||
|
||||
class CallVideoEncoder {
|
||||
public:
|
||||
// true — открыт проверенный кандидат; при отказе прежний encoder остаётся, причина в error().
|
||||
// HEVC TX — только Main tier; format заранее сверяет с CAPS владелец.
|
||||
bool open(const call_video_format& format, uint32_t bitrate, const QString& backend = {});
|
||||
void close();
|
||||
void close(); // Ждёт фоновой подготовки, отбрасывает буферизованный выход/pending-команду и очищает error().
|
||||
// true — одна команда принята; actual completion приходит из encode на границе KEY.
|
||||
bool requestBitrate(uint64_t generation, uint64_t request, uint32_t bitrate);
|
||||
// Пустое image забирает готовые кадры без нового input (pts игнорируется).
|
||||
// На false output пуст; completion нужно обработать и на ошибке. PTS готового выхода сохраняет capture PTS.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/* Desktop-видеозвонок: управление/camera/signals — GUI, encoder — TX, decoder — RX,
|
||||
* playout — отдельный display worker. start запускает workers; stop будит и присоединяет их,
|
||||
* закрывает port и очищает изображение. Нельзя вызывать stop из этих workers.
|
||||
* Между RX/display ограниченная SPSC очередь из трёх кадров, включая ожидающий GUI ACK. */
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
#include <QImage>
|
||||
@@ -16,8 +20,9 @@ public:
|
||||
// Блокирующая подготовка только в worker: один общий кэш encoder/decoder на процесс, без камеры и сети.
|
||||
// running отменяет ожидание/пробу; частичный результат не кэшируется. nullptr — без отмены.
|
||||
static call_video_caps capabilities(const std::atomic<bool>* running = nullptr);
|
||||
void start(quint64 id);
|
||||
void start(quint64 id); // Тот же работающий id — без изменений; другой id сначала останавливает прежний.
|
||||
void stop();
|
||||
// codec=0 — Auto; изменение режима асинхронно, результат/отказ сообщается statusChanged.
|
||||
void setVideo(bool enabled, const QByteArray& camera, int width, int height, int codec, int fps = 30);
|
||||
void setPresentationDriven(bool enabled); // GUI-поток: окно подтверждает рисование, иначе достаточно доставки signal.
|
||||
void imagePresented(quint64 serial); // GUI-поток; повторный/старый ACK безопасно игнорируется.
|
||||
|
||||
Reference in new issue
Block a user