Recomendaciones de capacidad — Streaming y grabación vstation
Documento de referencia con la arquitectura y dimensionamiento más óptimos para vstation, basado en las simulaciones stream-load-sim y mediamtx-recording-sim.
Resumen ejecutivo
| Capa | Recomendación principal |
|---|---|
| Streaming en vivo | LiveKit SFU + ingesta WHIP H.264 passthrough (sin transcode) |
| Grabación | MediaMTX con pull directo desde cámaras/NVR — sin relay ffmpeg en vstation |
| Separación | Dos pipelines independientes: vivo ≠ grabación |
| GPU servidor | No requerida en ninguno de los dos casos (stream copy / passthrough) |
Principio clave: no duplicar tráfico ni procesos. El relay ffmpeg en vstation es válido para decenas de cámaras; a escala (200 ingestas de vivo o 1300+ grabaciones) debe eliminarse o externalizarse.
Arquitectura recomendada
┌─────────────────────────────────────────────────────────────────────────┐
│ STREAMING EN VIVO │
│ Cámara RTSP ──► GStreamer WHIP (vstation) ──► LiveKit SFU ──► Browser │
│ Solo cámaras que alguien está viendo (on-demand ingest) │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ GRABACIÓN │
│ Cámara RTSP ──► MediaMTX (pull directo) ──► MinIO (segmentos) │
│ vstation: ILM, retención, playback — NO relay ffmpeg │
└─────────────────────────────────────────────────────────────────────────┘
1. Streaming en vivo (operadores)
Escenario de referencia
- 20 usuarios concurrentes
- 10 streams abiertos por usuario
- Streams distintos por operador (
--layout varied) → hasta 200 cámaras únicas en ingesta - Perfil: H.264 passthrough (
VSTATION_INGEST_MODE=whip, cámaras H.264 nativas)
Configuración óptima (.env)
VSTATION_INGEST_MODE=whip
VSTATION_H265_PASSTHROUGH=false # solo si cámaras H.265; preferir cámaras H.264 nativas
LIVEKIT_URL=wss://...
LIVEKIT_API_URL=https://...
LIVEKIT_API_KEY=...
LIVEKIT_API_SECRET=...
| Variable | Valor recomendado | Motivo |
|---|---|---|
VSTATION_INGEST_MODE | whip | Menor latencia que RTMP/ffmpeg legacy |
| Perfil de cámara | H.264 nativo | Evita transcode (~45% CPU/cámara con H.265) |
adaptiveStream / dynacast | false (ya en cliente) | Calidad fija para CCTV |
Dimensionamiento servidor (200 ingestas activas, H.264)
| Componente | RAM | CPU | GPU |
|---|---|---|---|
| Infra (app + postgres + redis + nginx) | 0.8 GB | 0.1 cores | 0% |
| Ingesta WHIP (200 cámaras) | 17.6 GB | 16 cores | 0% |
| LiveKit SFU (200 suscripciones) | 1.4 GB | 0.6 cores | 0% |
| Total servidor | ~27 GB | ~21 cores | 0% |
Servidor sugerido (con ~25% headroom): 34 GB RAM, 27 cores, 1 Gbps+ egress.
Clientes (20 operadores)
| Por operador | Total 20 usuarios |
|---|---|
| ~537 MB RAM navegador | ~10.7 GB |
| Decode GPU HW ~30% | ~600% agregado |
| 10 tiles × 720p–1080p | — |
Requisito operador: GPU con decode H.264 hardware, ≥8 GB RAM en el PC, Chrome/Edge recientes.
Ancho de banda streaming
| Dirección | Mbps |
|---|---|
| Ingress (cámaras → servidor) | ~300 |
| Egress (SFU → clientes) | ~300 |
Qué evitar en streaming
- Transcode H.265→H.264 con 200 cámaras activas → ~90 cores CPU solo en ingesta
- Mosaico compartido asumido cuando cada operador ve cámaras distintas — subestima ingesta 20×
- Firefox si alguna cámara usa H.265 passthrough (pantalla negra)
Simular otro escenario
npm run stream:sim
npx tsx scripts/stream-load-sim.ts --users 20 --streams 10 --layout varied
npx tsx scripts/stream-load-sim.ts --layout shared --unique-cameras 10 # videowall común
npx tsx scripts/stream-load-sim.ts --profile h265_transcode --cores 64 # peor caso HEVC
2. Grabación continua (MediaMTX)
Escenario de referencia
- 1300–1500 cámaras grabando simultáneamente
- Segmentos 30s (considerar 60s para reducir IOPS)
- Retención 30 días
- Bitrate 1000–1500 kbps por cámara
Configuración óptima — modo pull (implementado en vstation)
En vstation — vstation sincroniza paths en MediaMTX vía API cuando recordingEnabled=true:
VSTATION_RECORDING_MODE=pull
MEDIAMTX_RTSP_URL=
MEDIAMTX_API_URL=http://mediamtx:9997
MEDIAMTX_MINIO_ENDPOINT=http://minio:9000
MEDIAMTX_MINIO_ACCESS_KEY=...
MEDIAMTX_MINIO_SECRET_KEY=...
VSTATION_FASTSTART_WORKER=false
vstation crea/actualiza paths ingest/camera-{id} con source = URL RTSP de la cámara y record: true. No hace falta editar mediamtx.yml a mano por cada cámara.
Modo legacy relay: define VSTATION_RECORDING_MODE=relay y MEDIAMTX_RTSP_URL para volver al ffmpeg por cámara.
Prefijo MinIO: recordings/camera-{id}/. Grupos: recordingEnabled=true activa path + ILM en MinIO.
Dimensionamiento — topología directa (1400 cámaras @ 1000 kbps)
| Componente | RAM | CPU | GPU |
|---|---|---|---|
| vstation-app (sin relays) | 0.3 GB | 0.1 cores | 0% |
| MediaMTX (1400 paths) | 17 GB | 8 cores | 0% |
| MinIO | 3 GB | 1 core | 0% |
| Total servidor | ~20 GB | ~10 cores | 0% |
Servidor sugerido: 25 GB RAM, 13 cores, ≥3 Gbps red, NVMe/RAID (>500 MB/s sostenido).
Almacenamiento y red (1400 cámaras)
| Métrica | @ 1000 kbps | @ 1500 kbps |
|---|---|---|
| Escritura sostenida | ~175 MB/s | ~263 MB/s |
| TB/día | ~14 TB | ~22 TB |
| TB (30 días) | ~420 TB | ~650 TB |
| Segmentos/hora (30s) | ~168 000 | ~168 000 |
Ajuste de segmentos (sin código)
PATCH /api/mediamtx/config
Content-Type: application/json
{ "recordSegmentDuration": "60s" }
Reduce a la mitad los archivos/hora y la carga en MinIO.
Qué evitar en grabación
| Anti-patrón | Impacto (1400 cams) |
|---|---|
| Relay ffmpeg en vstation | ~48 GB RAM, ~25 cores extra + tráfico duplicado |
VSTATION_FASTSTART_WORKER=true a escala | Picos CPU por re-mux MP4 |
| Disco < 500 MB/s sostenido | Caídas de grabación |
| Segmentos 30s con 1500 cams | ~180k archivos/hora → IOPS alto |
Simular otro escenario
npm run recording:sim
npx tsx scripts/mediamtx-recording-sim.ts --cameras-min 1300 --cameras-max 1500
npx tsx scripts/mediamtx-recording-sim.ts --topology direct --bitrate-kbps 1000
npx tsx scripts/mediamtx-recording-sim.ts --segment-sec 60 --retention-days 30
3. Comparativa: relay vs directo (grabación)
| Aspecto | Relay ffmpeg (actual) | Directo MediaMTX (óptimo) |
|---|---|---|
| Procesos vstation | 1 × cámara | 0 |
| RAM vstation (1400 cams) | ~48 GB | ~0.3 GB |
| CPU vstation | ~25 cores | ~0.1 core |
| Saltos de red | Cámara → vstation → MTX | Cámara → MTX |
| Cambios de código | — | Ninguno (solo config) |
| Control on/off desde UI | Automático | Manual vía MediaMTX config/API |
4. Comparativa: streaming shared vs varied
| Aspecto | Mosaico compartido (10 cams) | Streams distintos por usuario (200 cams) |
|---|---|---|
| Suscripciones WebRTC | 200 | 200 |
| Ingestas activas | 10 | 200 |
| RAM servidor | ~3.4 GB | ~27 GB |
| CPU servidor | ~1.7 cores | ~21 cores |
| Egress | ~300 Mbps | ~300 Mbps |
La diferencia está en ingesta, no en el SFU. Si cada operador ve cámaras distintas, dimensionar por usuarios × streams.
5. Checklist de despliegue óptimo
Streaming
- Cámaras H.264 nativas donde sea posible
-
VSTATION_INGEST_MODE=whip - LiveKit SFU dimensionado separado del app server si >50 ingestas concurrentes
- Simular con
--layout variedsi operadores no comparten mosaico - PCs operador con GPU decode y ≥8 GB RAM
Grabación
-
VSTATION_RECORDING_MODE=pullyMEDIAMTX_RTSP_URLvacío -
MEDIAMTX_API_URLapuntando a MediaMTX :9997 - MinIO con NVMe, ILM por
recordings/camera-{id}/ -
recordSegmentDuration≥ 60s a escala -
VSTATION_FASTSTART_WORKER=falsecon >500 cámaras
Infraestructura compartida
- cadvisor (
:8080) + node-exporter para medir en staging - Validar plan/límites LiveKit antes de >150 suscripciones WebRTC
- Red dedicada o VLAN para tráfico RTSP (no mezclar con egress WebRTC sin QoS)
6. Matriz de decisión rápida
| Pregunta | Respuesta óptima |
|---|---|
| ¿Transcode H.265 en vivo? | No — usar cámaras H.264 o H.265 passthrough solo si todos los clientes lo soportan |
| ¿Relay ffmpeg para grabación? | No a partir de ~200 cámaras — pull directo en MediaMTX |
| ¿GPU en servidor? | No para passthrough/copy |
| ¿Dónde poner CPU de ingesta? | Servidor LiveKit/vstation separado del nodo MediaMTX/MinIO |
| ¿Cuánta red? | Streaming ~300 Mbps + Grabación ~1.5–2 Gbps (escala independiente) |
7. Scripts de validación
| Script | Comando | Uso |
|---|---|---|
| Streaming | npm run stream:sim | Operadores, LiveKit, ingesta WHIP |
| Grabación | npm run recording:sim | MediaMTX, MinIO, almacenamiento |
Ambos soportan --json, --help y parámetros CLI documentados.
Valores orientativos para CCTV 720p–1080p. Validar siempre en staging con métricas reales (cadvisor, iostat, MediaMTX /v3/paths/list).