Recorder
Services/Recorder создает готовую консолидированную запись конференции. Он использует общий с Services/Consolidator media pipeline, но направляет закодированный результат в MP4-файл, а не обратно на сервер.
Компоненты
flowchart LR
CAN[CAN recording.start]
Worker[Python recorder worker]
Native[Native Recorder]
Controller[Engine Controller]
Renderers[Consolidator renderers]
Mixers[Video and audio mixers]
Encoders[H.264 and AAC encoders]
MP4[conference and date.mp4]
Storage[VideoGrace Storage]
Index[records.db]
CAN --> Worker
Worker --> Native
Native --> Controller
Controller --> Renderers
Renderers --> Mixers --> Encoders --> MP4
MP4 --> Worker --> Storage --> Index
Python отвечает за оркестрацию:
- принимает
recording.startиrecording.stopчерез CAN; - запускает и останавливает нативный процесс;
- формирует
events.jsonl,report.jsonиreport.jsonl; - загружает артефакты в Storage;
- отправляет
artifact_readyи завершает CAN job.
Нативный процесс отвечает только за media plane:
- входит в конференцию короткоживущим bearer token сервисного аккаунта;
- принимает камеры, демонстрации и микрофоны участников;
- применяет серверную модерацию, VAD и раскладку Consolidator;
- микширует звук и видео;
- кодирует H.264 + AAC и записывает один MP4-файл.
Lifecycle
sequenceDiagram
participant S as VideoGrace Server
participant W as Python worker
participant R as Native Recorder
participant M as Shared Consolidator core
participant F as MP4 writer
S->>W: CAN recording.start + auth.access_token
W->>R: spawn, token in process environment
R->>S: bearer login + connect conference
S-->>R: members and remote media routes
R->>M: start renderers, mixers and encoders
M->>F: H.264 access units + AAC packets
F-->>W: conference and date.mp4
S->>W: job_cancel or recording.stop
W->>R: job-local stop marker
R->>F: finalize MP4
W->>S: upload + artifact_ready + complete
При штатной остановке Python создает job-local stop marker. Нативный процесс сначала останавливает capture threads и encoders, затем финализирует MP4. SIGTERM и принудительное завершение используются только как fallback. Такой порядок необходим, чтобы файл получил полную таблицу samples и корректно перематывался, в том числе при работе worker на Windows.
Временная шкала при перегрузке
Пропущенные дедлайны видеозахвата учитываются в RTP-часах H.264 (90 кГц). MP4 writer определяет длительность кадра по разнице соседних RTP timestamps, а не записывает каждый кадр как 40 мс независимо от пропусков. До поступления следующего кадра он хранит один закодированный кадр; при остановке последний кадр удерживается до конца аудиодорожки. Поэтому пропуски снижают плавность, но не ускоряют видео относительно звука.
Для PCM используются часы 48 кГц: пропуски заполняются тишиной перед кодированием
AAC, повторные и перекрывающиеся сэмплы отбрасываются. Длительность AAC-кадра
остаётся 1024 сэмпла. Дополнение последнего кадра может добавить около 21 мс.
В конце записи логируются video_ms, audio_ms и inserted_silence_ms.
Это устраняет накопление рассинхронизации из-за сокращения временной шкалы при
пропусках захвата, но не восстанавливает потерянные кадры или звук. Начальные
отсчёты аудио- и видеозахвата остаются независимыми; эта правка не обеспечивает
точную начальную синхронизацию и не исправляет ранее созданные файлы.
Регрессионная проверка: Tool/RecorderLoad/check_mp4_timing.py и нативная цель
Mp4TimingSmoke с CMake-опцией VG_BUILD_MP4_TIMING_TEST=ON.
Авторизация
CAN service token и CommandLoop access token имеют разное назначение:
VG_CAN_SERVICE_TOKENдает Python worker доступ к/can;payload.auth.access_tokenвыдается сервером для конкретного service account и используется нативным Recorder для CommandLoop и media routes.
Python передает access token через VG_ACCESS_TOKEN. Токен не включается в argv дочернего процесса и не логируется. Controller хранит входной bearer отдельно от access token, который сервер возвращает для media-каналов, поэтому reconnect обычных native-клиентов не меняет способ их авторизации.
Встроенная учетная запись recorder создается сервером автоматически после
появления первого обычного пользователя. Для нее создается скрытый клиент типа
Service с правами conference:join, media:render и storage:write.
Системный клиент не отображается в контактах и не учитывается в пользовательской
статистике. Если service_accounts.recorder уже настроен вручную, сервер
сохраняет существующую привязку.
Запуск
На старых Linux системный Python не обновляется: установщик при необходимости
скачивает отдельный проверенный SHA-256 runtime Python 3.12 в каталог сервера.
configure-recorder.py тоже выполняется через приватное окружение, а не через
системный python3. Рядом с рекордером в Linux/Windows x64 поставляется дежурный
транскрибер; подробнее в инструкции администратора.
В Linux-дистрибутив входят исходники worker и нативный бинарник. Установщик
создаёт отдельное Python-окружение, загружает зависимости через pip и
регистрирует vg-recorder.service. Unit связан с vgserver.service: обычный
start/stop/restart сервера распространяется на дежурный recorder.
Установщик включает секцию [CAN] и генерирует ServiceToken, только если токен
ещё не задан. Для выпуска короткоживущего media JWT рекордеру установщик также
создаёт отдельный [Auth] JwtSecret, если тот отсутствует. Оба секрета
сохраняются при обновлении. Worker читает CAN token непосредственно из
vgserver.conf. Локальное
подключение к wss://localhost/can допускает сертификат локального сервера;
для внешнего CAN endpoint следует установить VG_CAN_URL и
VG_RECORDER_STRICT_TLS=1 в /etc/videograce/recorder.env.
systemctl status vg-recorder
journalctl -u vg-recorder -f
Незагруженные записи находятся в /var/lib/videograce/recordings. Этот каталог
не удаляется деинсталлятором, чтобы ошибка upload или удаления пакета не привела
к потере записи.
На Windows установщик включает CAN в реестре, создаёт CAN service token и
отдельный JWT-secret, затем регистрирует задачу VideoGrace Standby Recorder в
Task Scheduler. Она работает от SYSTEM,
стартует вместе с Windows и запускает до четырёх нативных Recorder-процессов.
Рабочие файлы и лог находятся в
C:\ProgramData\IVS\VideoGrace\Server\recordings и
C:\ProgramData\IVS\VideoGrace\Server\logs\RecorderWorker.log.
export VG_CAN_URL=wss://join.example.com/can
export VG_CAN_SERVICE_TOKEN=change-me
export VG_SERVER_URL=https://join.example.com
export VG_NATIVE_RECORDER=/opt/VideoGrace/Server/Recorder
export VG_RECORDER_UPLOAD_TO_STORAGE=1
export VG_RECORDER_DELETE_AFTER_UPLOAD=1
./VIP/examples/06-recorder/run_recorder.sh \
--record-profile consolidated_native \
--output-dir /var/lib/videograce/recordings
Ручной запуск нативного процесса:
VG_ACCESS_TOKEN=eyJ... \
Recorder \
--server-url https://join.example.com \
--conference-tag ops \
--output /tmp/ops.mp4 \
--bitrate 2500
Опции --only-video и --only-audio оставляют один тип media. Через VG_H264_ENCODER можно выбрать поддерживаемый backend H.264 encoder.
Формат и артефакты
Профиль consolidated_native создает:
<название конференции> - <дата и время> UTC.mp4: H.264/AVC video track и AAC-LC audio track;events.jsonl: события lifecycle;report.json: итоговый отчет и список файлов;report.jsonl: журнал финализации.
Writer получает уже закодированные access units до RTP packetization. H.264 Annex-B преобразуется в AVCC samples, а SPS/PPS первого IDR сохраняются в AVCDecoderConfigurationRecord. Повторного video encode при упаковке файла нет.
После artifact_ready сервер сохраняет запись в records.db вместе с точным conference_session_id. Связывать запись, статистику и транскрипт только по conference_tag нельзя: одна и та же комната может иметь несколько сеансов.
После успешной загрузки и CAN-подтверждения artifact_ready/complete Python worker удаляет локальный каталог job. При ошибке записи, Storage или CAN каталог остается на диске для повторной доставки и диагностики. На стенде его можно сохранить и после успешной доставки параметром --keep-local-after-upload или VG_RECORDER_DELETE_AFTER_UPLOAD=0.
Инварианты
- Recorder не публикует consolidated device назад в конференцию.
- В файл не попадает RTP-представление самого Recorder.
- Основной результат job - один непустой MP4-файл с осмысленным именем.
- Нативный процесс не выполняет Storage upload и не знает CAN service token.
- Python не декодирует и не микширует production media.
- Worker объявляет серверу
max_concurrent_jobsи запускает отдельный нативный процесс для каждой активной конференции. В штатной поставке лимит равен4и меняется черезVG_RECORDER_MAX_CONCURRENT_JOBS; значение подбирают по CPU, RAM и дисковой нагрузке узла. - Ошибка или пустой output переводят запись в
failed, но worker все равно формирует диагностический report. - Локальные media и reports удаляются только после подтвержденной доставки; upload без успешного CAN completion не считается завершенным lifecycle.
Нагрузочный тест
Tool/RecorderLoad/recorder_load.py запускается на том же узле и под теми же cgroup/service limits, что production workers. Для каждой ступени он измеряет суммарные CPU/RSS, корректно останавливает процессы и проверяет MP4 через ffprobe на наличие H.264 и AAC. Тест следует проводить в отдельной конференции со стабильными реальными источниками; Storage throughput измеряется отдельно, так как upload начинается после финализации записи.
Legacy-профили
raw_tracks_v1 и decoded_audio сохранены для диагностики отдельных RTP-дорожек. Они не являются основным production-путем и не гарантируют готовый к просмотру единый файл конференции.