Skip to content

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-путем и не гарантируют готовый к просмотру единый файл конференции.