DEPLOY

Deploy Arq. 1

go2rtc relay integral — instalación, systemd, Docker, detección CSI/USB, monitoreo

Pre-Flight Checklist

#CheckComandoEsperado
1Cámara V4L2v4l2-ctl --list-devices/dev/video0 presente
2Formatos cámarav4l2-ctl -d /dev/video0 --list-formats-extMJPEG y/o H.264
3Micrófono ALSAarecord -lAl menos 1 card
4Puerto 1984 libress -tlnp | grep 1984(vacío)
5RAM disponiblefree -m | awk '/Mem/{print $7}'≥ 500 MB
6GPU mem (CSI)vcgencmd get_mem gpugpu=128M (si CSI con rpicam-vid exec)
7Binario descargado./go2rtc --versionv1.x.x

Instalación del Binario

# Descargar release ARM para RPi 3B 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 # Crear directorio de config 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

#!/bin/bash — hardware_detect.sh # Detecta tipo de cámara y ajusta go2rtc.yaml 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 # Detectar si USB tiene H.264 HW 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

# /etc/systemd/system/go2rtc.service [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 # Seguridad NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true ReadWritePaths=/run/go2rtc [Install] WantedBy=multi-user.target
# Activar sudo useradd -r -s /usr/sbin/nologin -G video,audio go2rtc sudo systemctl daemon-reload sudo systemctl enable --now go2rtc

Docker (alternativa)

# docker-compose.yml 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 # cámara (CSI o USB) - /dev/vchiq:/dev/vchiq # CSI VideoCore (si aplica) - /dev/snd:/dev/snd # audio ALSA 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)

// routes.js — proxy HTTP a go2rtc (cero carga media en Node) 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(); }; } // Registrar rutas 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étricaComando / EndpointUmbral alarma
Streams activoscurl -s localhost:1984/api/streams | jq 'length'= 0 (sin cámara)
Consumerscurl -s localhost:1984/api/streams | jq '.[].consumers | length'Informativo
RAM procesops -o rss= -p $(pgrep go2rtc) | awk '{print $1/1024"M"}'> 80 MB
CPUtop -bn1 -p $(pgrep go2rtc) | awk 'NR>7{print $9}'> 40%
GPU tempvcgencmd measure_temp> 70°C
Servicio activosystemctl is-active go2rtc!= active
MJPEG accesiblecurl -so /dev/null -w '%{http_code}' localhost:1984/api/frame.jpeg?src=solar-cam!= 200

Presupuesto de Recursos

ComponenteRAMCPU idleCPU 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