Sistema de streaming de vídeo en tiempo real con latencia ultra-baja (~100-300ms) utilizando WebRTC, FFmpeg y MediaMTX.
Trabajo Fin de Grado - Universidad Politécnica de Valencia
Diseño e implementación de un sistema de distribución de video de baja latencia basado en WebRTC y MediaMTX para realización en directo.
Este proyecto implementa un sistema completo de transmisión de vídeo con baja latencia utilizando el protocolo WebRTC. Permite capturar vídeo desde diferentes fuentes (cámaras USB, webcams, capturadoras, pantalla) y transmitirlo a través de un servidor web con visualización en tiempo real.
- Ultra baja latencia: ~100-300ms gracias a WebRTC
- Multi-protocolo: Soporte para WHIP/WHEP, RTSP, RTMP, HLS y SRT
- Multiplataforma: Scripts para Windows, Linux y Raspberry Pi
- Interfaz web: Reproductor integrado con controles intuitivos
- Containerizado: Despliegue sencillo con Docker Compose
- API de control: Gestión de streams mediante API HTTP
- Arquitectura
- Flujo de datos
- Requisitos
- Instalación
- Estructura del Proyecto
- Uso
- Puertos y Protocolos
- API MediaMTX
- Configuración avanzada
- Solución de problemas
- Desarrollo
- Tecnologías
- Licencia
- Autor
┌───────────────┐ ┌─────────────────┐ ┌─────────────────────────────────────┐ ┌─────────────────┐
│ FUENTES │ │ EMISORES │ │ SERVIDOR │ │ CLIENTES │
├───────────────┤ ├─────────────────┤ ├─────────────────────────────────────┤ ├─────────────────┤
│ │ │ │ WHIP │ │ WebRTC │ │
│ │───►│ FFmpeg (Win) │ ─────────────────► │ ┌─────────────┐ ┌───────────┐ │ ◄───────────────► │ Browser │
│ Cámaras │ │ │ │ │ │ │ │ │ │ │
│ │───►│ FFmpeg (RPi) │ ─────────────────► │ │ MediaMTX │◄───│ Node.js │ │◄── http://:80 │ OBS │
│ Micros │ │ │ │ │ (8889) │ │ Server │ │◄── https://:443 │ │
│ │───►│ Python/PYWHIP │ ─────────────────► │ │ │ │ │ │ │ .html │
│ Pantalla │ │ │ │ │ API:9997 ◄─┼────│ /api/ │ │ │ │
│ │───►│ Broadcaster │ ─────────────────► │ │ :9996 │ │ mediamtx │ │ │ │
│ ... │ │ (Browser WHIP) │ │ └─────────────┘ └───────────┘ │ │ VLC │
└───────────────┘ └─────────────────┘ └─────────────────────────────────────┘ └─────────────────┘
Proxy API: El servidor Node.js incluye un proxy en
/api/mediamtx/*que redirige peticiones a la API REST de MediaMTX (puerto 9997), evitando problemas de CORS.Playback proxy: El servidor Node.js incluye un proxy en
/api/playback/*para reenviar peticiones al servicio de grabaciones/reproducción (puerto 9996) sin exponer ese backend al navegador.HTTPS: El servidor soporta HTTPS (puerto 443) con certificados mkcert, necesario para usar
getUserMedia()desde cualquier dispositivo que acceda por IP (no localhost).
- Captura (Fuentes): Cámaras, micrófonos, pantalla o capturadoras proporcionan el contenido multimedia
- Emisión (WHIP): FFmpeg, scripts Python o el Broadcaster web codifican el vídeo y lo envían al servidor MediaMTX mediante protocolo WHIP
- Servidor (MediaMTX): Recibe los streams y los redistribuye a los clientes conectados
- Reproducción (WHEP): Los navegadores se conectan mediante WebRTC para visualización en tiempo real
- Docker Engine 20.10+
- Docker Compose v2.0+
- 2GB RAM mínimo
- Puertos disponibles: 80, 443, 1935, 8554, 8888, 8889, 8189/UDP, 9997
- FFmpeg compilado con soporte WebRTC/WHIP (ver
/docs/compilacion_todo_junto.md) - Cámara USB o capturadora compatible
- Raspberry Pi 4 (recomendado)
- FFmpeg compilado con soporte WebRTC/WHIP (ver
/docs/compilacion_ffmpeg_webrtc.md) - Cámara USB o módulo de cámara Pi
- Python 3.8+ (para scripts PYWHIP)
- Descargar el ffmpeg personalizado con openSSL y WHIP- (CLICK a wffmpeg)
git clone https://github.com/Luigiverde4/TFG.git
cd TFGPara emitir streams con WebRTC/WHIP desde Windows, necesitas una versión de FFmpeg compilada con soporte OpenSSL y WHIP:
Descomprime el archivo y añade la carpeta bin al PATH del sistema, o colócala en la raíz del proyecto.
Nota: Si prefieres compilar FFmpeg tú mismo, consulta
/docs/compilacion_todo_junto.md
Editar mediamtx/mediamtx.yml y configurar la IP de tu máquina para que los clientes WebRTC puedan conectarse:
# Buscar la línea webrtcAdditionalHosts y agregar tu IP local
webrtcAdditionalHosts: ['192.168.X.X'] # Cambiar por tu IPPara obtener tu IP:
# Windows
ipconfig
# Linux/macOS
ip addr show | grep inetNota: Esta configuración es necesaria para que los navegadores de otros dispositivos en la red local puedan reproducir el stream.
Para usar el Broadcaster desde cualquier dispositivo externo (otro PC, móvil, tablet), es necesario HTTPS. Los navegadores requieren un contexto seguro (HTTPS) para acceder a getUserMedia() (cámara/micrófono) cuando se accede por IP.
localhost funciona sin HTTPS, pero cualquier acceso por IP (192.168.x.x, etc.) requiere HTTPS.
# Windows (con Chocolatey)
choco install mkcert
# Windows (con Scoop)
scoop install mkcert
# macOS
brew install mkcert
# Linux (Debian/Ubuntu)
sudo apt install mkcert# Instalar CA local (solo la primera vez)
mkcert -install
# Generar certificados para localhost y tu IP
cd server
mkcert localhost 127.0.0.1 192.168.X.X # Cambiar por tu IP
# Renombrar los archivos generados
mv localhost+2.pem cert.pem
mv localhost+2-key.pem key.pemNota: Los certificados deben estar en la carpeta
server/con los nombrescert.pemykey.pem. Si no se encuentran, el servidor funcionará solo con HTTP.
docker compose up --buildPara ejecutar en segundo plano:
docker compose up -d --buildAcceder a las interfaces web:
| Interfaz | URL | Descripción |
|---|---|---|
| Index | http://localhost/ | Página principal con acceso a todas las herramientas |
| Broadcaster | https://localhost/broadcaster.html | Emitir stream desde el navegador (cámara/micrófono) |
| Player | http://localhost/player.html | Reproductor WebRTC principal |
| Playback | http://localhost/playback.html | Reproductor con controles avanzados |
| API Control | http://localhost/api-control.html | Panel de control de la API |
Acceso por IP: Para usar el Broadcaster desde otros dispositivos, usar
https://<IP_SERVIDOR>/broadcaster.html. HTTPS es obligatorio para acceder a la cámara cuando no es localhost.
TFG/
├── CAMS/ # Scripts de captura y streaming
│ ├── WHIP/ # Scripts FFmpeg para WebRTC (Windows)
│ │ ├── test.bat # Stream de prueba (testsrc)
│ │ ├── webcam.bat # Stream desde webcam
│ │ └── multi_*.bat # Configuraciones cámaras de Multi
│ ├── RTSP/ # Scripts para streaming RTSP
│ ├── SRT/ # Scripts para streaming SRT
│ └── PYWHIP/ # Scripts Python para streaming
│ ├── pywhip.py # Emisor WHIP en Python
│ └── pywhip_screen.py # Captura de pantalla
├── server/ # Servidor web Node.js
│ ├── server.js # Servidor Express (puerto 80)
│ └── public/ # Archivos estáticos
│ ├── index.html # Página principal
│ ├── broadcaster.html # Emisor WHIP desde navegador
│ ├── player.html # Reproductor principal
│ ├── playback.html # Reproductor avanzado
│ ├── api-control.html # Control de API
│ ├── js/ # Scripts JavaScript
│ │ ├── herramientas.js # Utilidades compartidas (escapeHtml, formatBytes)
│ │ ├── watchdog.js # Watchdog compartido de tráfico
│ │ ├── broadcaster-ui.js # UI y dispositivos del broadcaster
│ │ ├── broadcaster.js # Lógica de emisión WHIP/WebRTC
│ │ ├── playback-utils.js # Formateos específicos de playback
│ │ ├── playback-ui.js # UI y eventos del reproductor de grabaciones
│ │ ├── playback-core.js # Carga y reproducción de grabaciones
│ │ ├── playback.js # Bootstrap mínimo de playback
│ │ ├── player.js # Reproductor WHEP
│ │ └── api.js # Cliente y helpers de la API
│ └── css/ # Estilos CSS
├── mediamtx/ # Configuración de MediaMTX
│ ├── mediamtx.yml # Configuración del servidor
│ └── recordings/ # Directorio para grabaciones
├── docker/ # Dockerfiles
│ └── node/
│ └── Dockerfile # Imagen Node.js
├── rpi/ # Scripts para Raspberry Pi
│ ├── stream_mjpeg_high.sh # MJPEG 1080p con audio
│ ├── stream_yuv_high.sh # YUV 1080p con audio
│ └── multi_*.sh # # Configuraciones cámaras de Multi
├── docs/ # Documentación adicional
│ ├── compilacion_ffmpeg_webrtc.md
│ └── webRTC2webRTCLL.md
├── docker-compose.yml # Orquestación de servicios
└── README.md
cd CAMS/WHIP
.\test.batEsto envía un stream de prueba (testsrc + tono de audio) al servidor.
cd CAMS/WHIP
.\webcam.batcd rpi
chmod +x stream_mjpeg_high.sh
./stream_mjpeg_high.shEl Broadcaster permite emitir directamente desde cualquier dispositivo con navegador (PC, móvil, tablet):
- Abrir http://localhost/broadcaster.html (o
https://<IP>si accedes por IP) - Seleccionar la cámara y micrófono a usar
- Elegir la resolución deseada
- Introducir un nombre para el endpoint (ej:
cam1,movil) - Hacer clic en 🔴 Iniciar Transmisión
Nota: El Broadcaster utiliza el protocolo WHIP nativo del navegador para enviar el stream a MediaMTX, igual que FFmpeg pero sin necesidad de instalar software adicional.
Acceso por IP: Requiere HTTPS para acceder a la cámara. Usa
https://<IP>/broadcaster.htmlcon certificados mkcert. Desdelocalhostfunciona sin HTTPS.
El reproductor utiliza el protocolo WHEP (WebRTC-HTTP Egress Protocol) para recibir el stream con ultra baja latencia.
Desde el mismo equipo:
- Abrir http://localhost/player.html
- Configurar el nombre del stream (por defecto:
whipLL) - Hacer clic en ▶ Reproducir
Desde otro dispositivo en la red:
- Abrir
http://<IP_SERVIDOR>/player.html(ej: http://192.168.1.100/player.html) - Configurar el servidor con la IP del equipo que ejecuta MediaMTX
- Hacer clic en ▶ Reproducir
Visualización directa via WHEP (sin interfaz web):
También puedes acceder directamente al stream via WHEP desde cualquier cliente compatible:
http://<IP_SERVIDOR>:8889/<nombre_stream>
Ejemplo: http://192.168.1.100:8889/whipLL
El reproductor de grabaciones consulta primero la API de control de MediaMTX para listar los paths grabados y después usa el servicio de playback para obtener los fragmentos fMP4.
Desde el mismo equipo:
- Abrir http://localhost/playback.html
- Elegir un stream grabado
- Seleccionar el punto de inicio o el modo lookback
- Pulsar ▶ Reproducir desde tiempo seleccionado
Desde otro dispositivo en la red:
- Abrir
http://<IP_SERVIDOR>/playback.html - Cargar la lista de grabaciones
- Reproducir la grabación deseada
Tecnología: Playback no usa WebRTC. El navegador recibe vídeo fMP4 por HTTP y lo reproduce con el elemento
<video>.
| Puerto | Protocolo | Descripción | Latencia |
|---|---|---|---|
| 80 | HTTP | Servidor web (interfaz) | - |
| 443 | HTTPS | Servidor web seguro (requerido para getUserMedia por IP) | - |
| 1935 | RTMP | Streaming RTMP | ~2-5s |
| 8554 | RTSP | Streaming RTSP | ~1-2s |
| 8888 | HTTP | HLS (HTTP Live Streaming) | ~6-30s |
| 8889 | HTTP | WebRTC (WHIP/WHEP) | ~100-300ms |
| 8189 | UDP | WebRTC ICE/STUN | - |
| 9997 | HTTP | API REST MediaMTX | - |
| 9996 | HTTP | Playback server (fMP4) | - |
La API REST está disponible en el puerto 9997:
# Listar streams activos
curl http://localhost:9997/v3/paths/list
# Obtener información de un stream
curl http://localhost:9997/v3/paths/get/whipLL
# Estadísticas del servidor
curl http://localhost:9997/v3/hlsmuxers/listNota: En la interfaz web, estas llamadas se exponen a través del proxy
/api/mediamtx/*. Las grabaciones y reproducción se exponen mediante/api/playback/*.
-c:v libx264
-preset ultrafast
-tune zerolatency
-profile:v baseline
-level 3.1
-g 30 # GOP size (keyframe cada 30 frames)
-bf 0 # Sin B-frames
-x264-params "keyint=30:min-keyint=30:no-scenecut=1"-c:a libopus
-b:a 128k
-ar 48000
-ac 2
-application lowdelay- Verificar que MediaMTX está corriendo:
docker compose ps - Comprobar logs:
docker compose logs mediamtx - Verificar que FFmpeg está enviando correctamente: buscar "WHIP session established" en los logs
- Usar preset
ultrafasten FFmpeg - Activar
zerolatencytune - Reducir GOP size (
-g 30) - Desactivar B-frames (
-bf 0)
- Verificar que el puerto UDP 8189 está abierto
- Comprobar configuración de firewall
- En redes NAT, puede ser necesario configurar STUN/TURN
# Windows - Listar dispositivos
ffmpeg -list_devices true -f dshow -i dummy
# Linux - Listar dispositivos
v4l2-ctl --list-devices
# Python
python CAMS/list_devices.pydocker compose down
docker compose up --builddocker compose logs -fdocker exec -it server sh
docker exec -it mediamtx sh| Tecnología | Uso |
|---|---|
| MediaMTX | Servidor de medios multi-protocolo |
| FFmpeg | Codificación y transmisión de vídeo |
| WebRTC | Comunicación en tiempo real (WHIP/WHEP) |
| Node.js | Servidor web |
| Express | Framework web |
| Docker | Contenedorización |
Este proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para más detalles.
Ricardo Román Martínez
Trabajo Fin de Grado - Universidad Politécnica de Valencia (UPV)
Desarrollado como parte del Trabajo Fin de Grado en la UPV