Real-Time Backend
Recording
Zoom записывает 300+ млн минут встреч в день. Как хранить это без потерь при падениях серверов?
- **Zoom Cloud Recording** - composite запись с grid layout, сегментируется каждые 2 ГБ, пишется прямо в S3, пользователь получает ссылку через 5-10 минут после встречи
- **Riverside.fm** - individual tracks записываются локально на каждом участнике как резервная копия, параллельно стримятся на сервер - потеря соединения не прерывает запись
- **Google Meet** - cloud recording уходит сразу в Google Drive, транскрипция добавляется автоматически через Speech-to-Text, файл доступен всем с правами на встречу
Server Recording
Когда запись ведётся на клиенте - браузер собирает WebM/MP4 локально через MediaRecorder и потом загружает файл. Это ненадёжно: вкладка закрылась, интернет упал - запись потеряна. **Server-side recording** перекладывает ответственность на инфраструктуру: медиасервер (Kurento, Janus, mediasoup + custom) сам принимает RTP-потоки и пишет их в файл, независимо от состояния клиентов.
Архитектурно это выглядит так: SFU или MCU уже держит все входящие RTP-потоки. Добавляется recording pipeline - GStreamer или FFmpeg процесс, который читает эти потоки через internal bus и mux'ит в контейнер. Zoom использует именно этот подход: запись хранится на серверах Zoom, а не зависит от компьютера хоста.
Google Meet записывает ~100 млн минут встреч в день. Запись идёт на уровне инфраструктуры - клиент только получает ссылку на готовый файл через Drive.
- **Client-side recording** - MediaRecorder в браузере, ненадёжно при обрывах
- **Server-side recording** - SFU/MCU пишет RTP напрямую, клиент не участвует
- **Cloud recording** - файл сразу пишется в S3/GCS, без промежуточного диска
Чем server-side recording принципиально отличается от client-side MediaRecorder?
Composite Recording
**Composite recording** (смешанная запись) - все участники конференции сводятся в один видеопоток перед записью. MCU (Multipoint Control Unit) рендерит grid layout: 2x2, 3x3 - и пишет один MP4 файл. Это то, что видит пользователь после «Записать встречу» в Zoom или Teams.
Технически: на сервере запускается compositor - GStreamer videomixer или FFmpeg filter_complex, который принимает декодированные кадры от каждого участника, накладывает по сетке и кодирует обратно в H.264. Это CPU-интенсивная операция: для 9-участниковой встречи нужно декодировать 9 VP8/VP9 потоков и закодировать 1 H.264.
Zoom тратит ~10x больше CPU на composite recording по сравнению с individual tracks. Поэтому в их SLA cloud recording имеет отдельные лимиты по минутам - это дорогая операция.
Почему composite recording требует значительно больше CPU на сервере, чем individual tracks?
Individual Tracks
**Individual tracks** (раздельная запись) - каждый участник пишется в отдельный файл. Сервер не декодирует медиа вообще: RTP пакеты от участника X просто demux'ятся и кладутся в participant_X.webm. CPU нагрузка минимальна, диска нужно N раз больше.
Это формат для post-production: видеоредактор (DaVinci Resolve, Premiere) получает отдельные треки и монтирует по своему усмотрению. Loom, Riverside.fm и Descript строят весь бизнес на individual tracks - пользователь записывает подкаст из 4 человек, каждый в своём треке, потом редактор убирает паузы, добавляет transitions.
Riverside.fm записывает individual tracks локально на каждом участнике (резервная копия) и одновременно стримит на сервер. При потере соединения - загрузка возобновляется с точки обрыва. Это позволяет записывать подкасты с гостями через slow 3G без потерь.
В каком случае individual tracks предпочтительнее composite?
Recording Storage
Запись на диск медиасервера - только промежуточный шаг. Финальный файл должен оказаться в объектном хранилище (S3, GCS, R2) для надёжности и доступа пользователей. Стандартный pipeline: `write to local /tmp` -> `upload to S3 on finalize` -> `delete local file` -> `trigger transcoding job`.
Для long-running meetings (8+ часов в enterprise Zoom) файл не помещается в RAM и пишется сегментами - каждые 2 ГБ или 30 минут. После завершения встречи сегменты конкатенируются. AWS MediaConvert или Elastic Transcoder потом генерируют несколько quality-уровней для adaptive playback.
Cloudflare R2 vs S3 для записей: R2 не берёт плату за egress - при 1M минут записей в месяц это $10K+ экономии на скачивании. Поэтому большинство стартапов в video conferencing пространстве переходят на R2 для recording storage.
Cloud recording = автоматически надёжно, файл точно сохранится
Cloud recording надёжен только при правильном pipeline: локальная запись -> инкрементальный upload -> подтверждение сегментов -> финализация
Если медиасервер падает в середине встречи без checkpoint'ов, FFmpeg process умирает с незакрытым контейнером - файл окажется corrupted. Нужны сегменты и resumable upload.
Зачем сегментировать запись каждые 30 минут вместо одного большого файла?
Итоги
- **Server-side recording** надёжнее client-side: продолжается при отключении клиентов, не зависит от браузера
- **Composite** - один файл для всех участников, дорого по CPU (transcode каждого потока); **Individual tracks** - N файлов, дёшево по CPU, идеально для монтажа
- **Pipeline**: local write -> S3 upload -> transcoding -> notify; сегментирование каждые 30 мин защищает от потери данных при сбоях
Связанные темы
Recording опирается на медиаинфраструктуру и тесно связан с доставкой контента:
- SFU vs MCU — MCU необходим для composite recording на сервере; SFU + client-side compositor дешевле
- Adaptive Bitrate — Transcoding после записи генерирует несколько quality уровней для ABR playback
- Object Storage (S3) — Финальное хранилище для всех записей - egress cost определяет выбор провайдера
Вопросы для размышления
- Riverside.fm и Zoom оба предлагают «cloud recording» - в чём принципиальная разница их подходов к надёжности записи?
- При composite recording с 50 участниками сервер декодирует 50 потоков и кодирует 1. Как изменится нагрузка если использовать hardware encoding (NVENC)?
- Для подкаст-платформы с 10K одновременных записей по 2 человека - считать стоимость хранения individual vs composite за месяц при средней встрече 60 минут.