Skip to content

Recorder

Services/Recorder — пример receiver bot. Он логинится как ClientType::Recorder, входит в конференцию, создает renderer sessions для remote devices и записывает media в файл.

Назначение

Recorder показывает, как писать клиентов, которые не публикуют свои устройства, а принимают чужие потоки: запись, аналитика, speech-to-text, AI observers, monitoring.

Запуск:

Recorder <server> <login> <password> <conference_tag> <path> <mp3Mode>

Пример из кода:

Recorder localhost:443 recorder 1 default ~/records/ 0

Entry point: Services/Recorder/Recorder.cpp.

Архитектура

flowchart TB
    Main[Recorder.cpp]
    Controller[Engine/Controller]
    Processor[Services/Recorder/Processor]
    MemberList[MemberList]
    RecorderCore[Record/Recorder]
    Audio[RendererAudioSession]
    Video[RendererVideoSession]

    Main --> Controller
    Main --> MemberList
    Main --> Processor
    Processor --> Controller
    Processor --> MemberList
    Processor --> RecorderCore
    Processor --> Audio
    Processor --> Video
    Audio --> RecorderCore
    Video --> RecorderCore

Lifecycle

sequenceDiagram
    participant Main
    participant P as Recorder Processor
    participant C as Controller
    participant S as Server
    participant R as Recorder core
    participant RS as RendererSession

    Main->>P: SetParams + Start
    P->>C: Connect(login/password, ClientType=Recorder)
    S-->>C: connect_response OK
    C-->>P: LogonSuccess
    P->>C: ConnectToConference(tag)
    S-->>C: connect_to_conference_response OK
    C-->>P: ConferenceConnected
    P->>R: Start(fileName, mp3Mode)
    S-->>C: device_connect remote camera/mic
    C-->>P: DeviceConnect
    P->>RS: create renderer, set codec/name/client/port
    P->>RS: Start(receiverSSRC, authorSSRC, deviceId, secureKey)
    RS-->>R: decoded audio/video frames

Remote device handling

Recorder обрабатывает DeviceConnect:

  • Camera и Demonstration создают RendererVideoSession;
  • Microphone создает RendererAudioSession;
  • устройства Consolidator пропускаются;
  • DeviceDisconnect удаляет соответствующую renderer session.
flowchart TB
    Event[DeviceConnect]
    Type{device_type}
    Video[RendererVideoSession]
    Audio[RendererAudioSession]
    Params[Set RTP params, codec, name, client_id]
    Start[Start receiver_ssrc/author_ssrc/device_id/secure_key]
    Record[Recorder writes MKV/MP3]

    Event --> Type
    Type -->|Camera/Demonstration| Video
    Type -->|Microphone| Audio
    Video --> Params --> Start --> Record
    Audio --> Params

Media pipeline

Video:

flowchart LR
    RTP[RTPSocket]
    Decrypt[Decryptor]
    Collect[VP8RTPCollector]
    Decode[VP8 Decoder]
    Rec[Recorder]

    RTP --> Decrypt --> Collect --> Decode --> Rec

Audio:

flowchart LR
    RTP[RTPSocket]
    Decrypt[Decryptor]
    Decode[Opus Decoder]
    Rec[Recorder]

    RTP --> Decrypt --> Decode --> Rec

Что копировать для своего receiver/AI observer

  1. Логиниться отдельным сервисным пользователем.
  2. Использовать ClientType::Recorder или другой согласованный тип.
  3. На ConferenceConnected запускать свой processing core.
  4. На каждый remote device_connect создавать session по типу устройства.
  5. Передавать в session receiverSSRC, authorSSRC, port, codec, secureKey.
  6. На device_disconnect обязательно останавливать и удалять session.
  7. На пустую конференцию можно отключаться через MemberList::CheckNoUsers().

Инварианты

  • Recorder не должен создавать capturer devices.
  • Один remote device соответствует одной renderer session.
  • receiverSSRC используется для приема потока.
  • authorSSRC нужен для RTCP/управляющих сообщений к автору.
  • Запись должна останавливаться при выходе из конференции или потере всех участников.

VIP Python recorder и storage report

Новый service-worker путь находится в VIP/examples/06-recorder/recorder.py. Он работает через CAN, входит в CommandLoop сервисным аккаунтом и пишет один из профилей:

  • decoded_audio - сегменты .wav/.mp3 для микрофонов.
  • raw_tracks_v1 - отдельные дорожки по device_id: Ogg/Opus для звука, H.264 Annex-B или IVF/VP8 для видео и демонстраций.

После остановки worker всегда создает:

  • events.jsonl - timeline событий записи, участников, VAD и stream lifecycle.
  • manifest.json - track-level описание для raw_tracks_v1.
  • report.json - итоговый machine-readable отчет для UI и последующей индексации.
  • report.jsonl - append-only журнал финализации отчета.

Если job содержит storage_required=true или worker запущен с --upload-to-storage, recorder загружает артефакты в storage через chunked upload и отправляет CAN artifact_ready:

{
  "schema": "videograce.recording.artifact.v1",
  "report_blob_id": "...",
  "report_url": "https://join.example.com/api/storage/blobs/...",
  "manifest_blob_id": "...",
  "events_blob_id": "...",
  "recording": {
    "job_id": "...",
    "conference_tag": "demo",
    "profile": "raw_tracks_v1",
    "status": "completed",
    "started_at_ms": 1780000000000,
    "ended_at_ms": 1780000300000,
    "duration_ms": 300000
  },
  "files": [
    {
      "path": "tracks/audio/device_1001.ogg",
      "blob_id": "...",
      "url": "https://join.example.com/api/storage/blobs/...",
      "size": 123456,
      "content_type": "audio/ogg",
      "sha256": "...",
      "scope": "manual"
    }
  ]
}

Для recorder upload используется X-Upload-Scope: manual, чтобы файлы не удалялись orphan GC, который чистит временные вложения со scope upload.

Когда сервер получает artifact_ready по CAN, он сохраняет нормализованный индекс записи в records.db. Экран web-client Записи читает GET /api/v1.0/recordings; CAN job list используется только как оперативный контур worker'ов и может очищаться по TTL без потери архива.