Pre-Flight Checklist
| # | Check | Comando | Esperado |
| 1 | Cámara V4L2 | v4l2-ctl --list-devices | /dev/video0 presente |
| 2 | Formatos cámara | v4l2-ctl -d /dev/video0 --list-formats-ext | MJPEG y/o H.264 |
| 3 | Micrófono ALSA | arecord -l | Al menos 1 card |
| 4 | Puerto 1984 libre | ss -tlnp | grep 1984 | (vacío) |
| 5 | RAM disponible | free -m | awk '/Mem/{print $7}' | ≥ 500 MB |
| 6 | GPU mem (CSI) | vcgencmd get_mem gpu | gpu=128M (si CSI con rpicam-vid exec) |
| 7 | Binario descargado | ./go2rtc --version | v1.x.x |
Instalación del Binario
VERSION="1.9.9"
wget -q "https://github.com/AlexxIT/go2rtc/releases/download/v${VERSION}/go2rtc_linux_arm"
chmod +x go2rtc_linux_arm
sudo mv go2rtc_linux_arm /usr/local/bin/go2rtc
sudo mkdir -p /etc/go2rtc
sudo cp go2rtc.yaml /etc/go2rtc/go2rtc.yaml
Binario Go estático: sin dependencias de runtime. ARM (32-bit) es compatible con RPi 3B (BCM2837 ARMv8 ejecutando OS 32-bit).
Detección CSI / USB
CONFIG_DIR="/etc/go2rtc"
if libcamera-hello --list-cameras 2>/dev/null | grep -q "Available cameras: [1-9]"; then
echo "[detect] CSI → go2rtc con exec:rpicam-vid (HW encode)"
cp "$CONFIG_DIR/go2rtc-csi.yaml" "$CONFIG_DIR/go2rtc.yaml"
elif [ -e /dev/video0 ]; then
if v4l2-ctl -d /dev/video0 --list-formats-ext 2>/dev/null | grep -q "H.264"; then
echo "[detect] USB con H.264 HW → passthrough"
cp "$CONFIG_DIR/go2rtc-usb-h264.yaml" "$CONFIG_DIR/go2rtc.yaml"
else
echo "[detect] USB MJPEG → standard V4L2"
cp "$CONFIG_DIR/go2rtc-usb.yaml" "$CONFIG_DIR/go2rtc.yaml"
fi
else
echo "[detect] ERROR: No camera found"
exit 1
fi
A diferencia de MediaMTX (Arq. 2), go2rtc soporta ambos tipos de cámara de forma nativa. La detección solo optimiza la config, no cambia la arquitectura.
Unidad systemd
[Unit]
Description=go2rtc Media Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=go2rtc
Group=video,audio
ExecStartPre=/usr/local/bin/hardware_detect.sh
ExecStart=/usr/local/bin/go2rtc -config /etc/go2rtc/go2rtc.yaml
Restart=on-failure
RestartSec=5
MemoryMax=100M
CPUQuota=50%
StandardOutput=journal
StandardError=journal
SupplementaryGroups=video audio
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/run/go2rtc
[Install]
WantedBy=multi-user.target
sudo useradd -r -s /usr/sbin/nologin -G video,audio go2rtc
sudo systemctl daemon-reload
sudo systemctl enable --now go2rtc
Docker (alternativa)
services:
go2rtc:
image: alexxit/go2rtc:latest
restart: unless-stopped
network_mode: host
volumes:
- /etc/go2rtc/go2rtc.yaml:/config/go2rtc.yaml:ro
devices:
- /dev/video0:/dev/video0
- /dev/vchiq:/dev/vchiq
- /dev/snd:/dev/snd
group_add:
- video
- audio
mem_limit: 100m
cpus: 0.5
network_mode: host evita port mapping complejo. /dev/snd expone ALSA para captura de audio. /dev/vchiq solo necesario si CSI con rpicam-vid exec.
Proxy Oasis (Node.js)
const http = require('http');
function proxyGo2rtc(localPath, go2rtcPath) {
return (req, res) => {
const proxyReq = http.request(
{ hostname: '127.0.0.1', port: 1984, path: go2rtcPath, method: 'GET' },
(proxyRes) => {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res);
}
);
proxyReq.on('error', () => res.status(502).end());
proxyReq.end();
};
}
app.get('/video.mjpeg', proxyGo2rtc('/video.mjpeg', '/api/frame.mjpeg?src=solar-cam'));
app.get('/video.m3u8', proxyGo2rtc('/video.m3u8', '/api/stream.m3u8?src=solar-cam'));
app.get('/snapshot', proxyGo2rtc('/snapshot', '/api/frame.jpeg?src=solar-cam'));
Node.js actúa como proxy puro (pipe). Cero buffer en memoria, cero CPU de media. El browser ve rutas limpias bajo el dominio Oasis.
Estrategia de Fallback
┌──────────────────────────────────────────────┐
│ hardware_detect.sh │
│ │
│ ¿V4L2 detectado? │
│ SÍ → go2rtc (Arq. 1) ← ESTÁNDAR │
│ NO → ERROR (sin cámara) │
│ │
│ Fallback de servicio: │
│ go2rtc crash (3×) → µStreamer (MJPEG) │
│ µStreamer crash → snapshot cron │
│ │
│ Escalado futuro: │
│ Fase 3 + Tor → migrar a Arq. 3 (Mumble) │
│ Solo LAN + RAM mín → Arq. 6 (µStreamer) │
└──────────────────────────────────────────────┘
Monitoreo
| Métrica | Comando / Endpoint | Umbral alarma |
| Streams activos | curl -s localhost:1984/api/streams | jq 'length' | = 0 (sin cámara) |
| Consumers | curl -s localhost:1984/api/streams | jq '.[].consumers | length' | Informativo |
| RAM proceso | ps -o rss= -p $(pgrep go2rtc) | awk '{print $1/1024"M"}' | > 80 MB |
| CPU | top -bn1 -p $(pgrep go2rtc) | awk 'NR>7{print $9}' | > 40% |
| GPU temp | vcgencmd measure_temp | > 70°C |
| Servicio activo | systemctl is-active go2rtc | != active |
| MJPEG accesible | curl -so /dev/null -w '%{http_code}' localhost:1984/api/frame.jpeg?src=solar-cam | != 200 |
Presupuesto de Recursos
| Componente | RAM | CPU idle | CPU stream |
| go2rtc (V4L2 + ALSA) | ~20–40 MB | ~1% | ~5–20% |
| Oasis (Node.js) | ~80 MB | ~2% | ~1% (proxy) |
| node-datachannel (opcional) | ~20 MB | ~0% | ~5% |
| Sistema (Bookworm) | ~120 MB | — | — |
| TOTAL | ~240–260 MB | ~3% | ~11–26% |
| Disponible en 1 GB: ~740–760 MB libres |
Similar a Arq. 2 (~250 MB), pero sin restricción de cámara: CSI y USB sin cambio de arquitectura. CPU más variable porque depende de re-encode.
deploy
systemd
Docker
hardware_detect
V4L2
ALSA
fallback
monitoreo
proxy
~250 MB