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
- Логиниться отдельным сервисным пользователем.
- Использовать
ClientType::Recorderили другой согласованный тип. - На
ConferenceConnectedзапускать свой processing core. - На каждый remote
device_connectсоздавать session по типу устройства. - Передавать в session
receiverSSRC,authorSSRC,port, codec,secureKey. - На
device_disconnectобязательно останавливать и удалять session. - На пустую конференцию можно отключаться через
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 без потери архива.