29 KiB
Prompt de integración WHIP para Restreamer UI
Este documento es el brief técnico completo para adaptar la UI de Restreamer de modo que exponga el servidor WHIP del Core de la misma manera que ya expone RTMP Server. Incluye dos casos de uso: WHIP Server (panel de estado + URL para OBS) y WHIP Receive mode (fuente de entrada en un proceso).
Contexto del Core
El Core de datarhei expone las siguientes APIs relevantes:
Config (GET /api/v3/config)
La sección whip dentro de config tiene esta forma:
"whip": {
"enable": true,
"address": ":8555",
"token": ""
}
El campo address puede ser :8555 (solo puerto) o host:8555.
Canales activos (GET /api/v3/whip)
Devuelve la lista de streams que OBS/clientes están publicando en este momento:
[
{
"name": "9VGyRCkCVCgj",
"published_at": "2026-03-14T02:51:36.799550551Z"
}
]
URL de publicación WHIP (para OBS)
http://<host>:<whip_port>/whip/<stream_key>
Ejemplo: http://192.168.1.15:8555/whip/mistream
URL de relay SDP (para FFmpeg / proceso interno)
http://localhost:<whip_port>/whip/<stream_key>/sdp
Esta es la URL que el Core inyecta automáticamente con {whip} como placeholder de input.
Placeholder de proceso
En el config de un proceso de FFmpeg, el input address puede usar:
{whip:name=<stream_key>}
El Core lo expande a http://localhost:<whip_port>/whip/<stream_key>/sdp y agrega automáticamente -protocol_whitelist file,crypto,http,rtp,udp,tcp en las input options.
Parte 1 — Panel "WHIP Server" (equivalente a "RTMP Server")
Dónde ubicarlo
En la misma sección de configuración/status donde aparece RTMP Server y SRT Server. Añadir una tarjeta o sección llamada WHIP Server.
Comportamiento esperado
1.1 Indicador de estado
- Leer
config.whip.enabledeGET /api/v3/config - Mostrar badge Enabled (verde) / Disabled (gris)
- Mostrar el puerto: extraer de
config.whip.address(ej.:8555→8555)
1.2 Campo "Stream URL para OBS"
Construir la URL de publicación a mostrar:
http://<host_público>:<whip_port>/whip/
host_público: usarconfig.host.name[0]siconfig.host.auto === true, o el primer valor deconfig.host.namewhip_port: extraer deconfig.whip.address- Mostrar un campo de texto read-only con botón Copiar
- Indicar al usuario que al final de la URL debe agregar su stream key (ej.
mistream)
Ejemplo visual:
┌─────────────────────────────────────────────────────┐
│ WHIP Server URL │
│ http://192.168.1.15:8555/whip/ [Copiar] │
│ Ingresá tu stream key al final de la URL │
└─────────────────────────────────────────────────────┘
1.3 Campo "Stream Key"
- Input de texto editable donde el usuario escribe su stream key (ej.
mistream,obs-live, etc.) - Al escribirla, actualizar dinámicamente la URL completa a copiar:
http://192.168.1.15:8555/whip/mistream - Botón Copiar URL completa
1.4 Token de autenticación (opcional)
- Si
config.whip.tokenno está vacío, mostrar un aviso: "Este servidor requiere un token. Agregá?token=<token>al final de la URL." - Opcionalmente, ofrecer un checkbox "Incluir token en la URL" que lo appende automáticamente.
1.5 Tabla de streams activos
- Hacer polling a
GET /api/v3/whipcada 5 segundos - Mostrar una tabla con columnas: Stream Key | Publicando desde
- Si la lista está vacía, mostrar "Sin streams activos"
┌──────────────────┬──────────────────────┐
│ Stream Key │ Publicando desde │
├──────────────────┼──────────────────────┤
│ 9VGyRCkCVCgj │ hace 3 minutos │
│ mistream │ hace 12 segundos │
└──────────────────┴──────────────────────┘
1.6 Configuración WHIP (settings)
Sección colapsable o en la misma pantalla de configuración del Core:
- Toggle Habilitar WHIP Server → modifica
config.whip.enable - Campo Puerto WHIP → modifica
config.whip.address(ej.8555) - Campo Token → modifica
config.whip.token - Campo Puerto relay RTSP interno → modifica
config.whip.rtsp_address(ej.8554)- Mostrar tooltip: "Habilita el relay RTSP para que múltiples procesos FFmpeg puedan consumir el mismo stream WHIP simultáneamente. Dejar vacío para desactivar."
- Si está vacío: el relay RTSP está desactivado; solo un proceso puede leer el stream con
{whip}. - Si está configurado: múltiples procesos pueden usar
{whip-rtsp}como input.
Parte 2 — Modo de recepción WHIP ("Receive mode")
Dónde ubicarlo
En el wizard de creación/edición de un proceso (input source selector), junto a las opciones existentes como:
- Network Source (HLS, RTP, RTSP, etc.)
- RTMP Source
- SRT Source
- → WHIP Source ← (nueva opción)
Comportamiento esperado
2.1 Selector de protocolo
En la lista de protocolos de entrada agregar la opción:
WHIP (WebRTC HTTP Ingestion Protocol)
2.2 Formulario de configuración de la fuente WHIP
Al seleccionar WHIP como fuente, mostrar:
Stream Key — campo de texto editable
(Se recomienda usar el ID del proceso por defecto, o dejar que el usuario lo cambie)
URL de publicación para OBS (campo read-only + botón Copiar):
http://<host_público>:<whip_port>/whip/<stream_key>
Actualiza en tiempo real mientras el usuario escribe el stream key.
┌──────────────────────────────────────────────────────┐
│ Protocolo de entrada: WHIP │
│ │
│ Stream Key: [mistream ] │
│ │
│ URL para OBS (copiar en OBS → Servicio WHIP): │
│ http://192.168.1.15:8555/whip/mistream [Copiar] │
│ │
│ Estado: ● Esperando publicador... │
│ ✓ Transmitiendo (si hay publisher activo) │
└──────────────────────────────────────────────────────┘
2.3 Indicador de estado en tiempo real
- Consultar
GET /api/v3/whipcada 5 segundos - Si
name === stream_keyaparece en la lista → mostrar "✓ Transmitiendo" (verde) - Si no → "● Esperando publicador..." (gris/naranja)
2.4 Address interno del proceso
Cuando el usuario guarda el proceso, el input.address debe configurarse como:
{whip:name=<stream_key>}
El Core lo expande a http://localhost:<whip_port>/whip/<stream_key>/sdp y agrega -protocol_whitelist automáticamente.
Si la UI no soporta el placeholder {whip}, puede usar directamente:
http://localhost:<whip_port>/whip/<stream_key>/sdp
Y agregar en input.options (ANTES de la URL, es decir, al principio del array):
["-protocol_whitelist", "file,crypto,http,rtp,udp,tcp"]
IMPORTANTE: -protocol_whitelist file,crypto,http,rtp,udp,tcp debe estar presente para que FFmpeg pueda abrir los sub-protocolos RTP/UDP anidados dentro de la URL HTTP. Sin esta opción el probe devuelve 0x0 none.
2.5 Probe automático
Cuando el usuario hace clic en "Probe" o "Detectar streams":
- La UI puede invocar
GET /api/v3/process/<id>/probesi el proceso ya existe - O construir un payload de probe temporal equivalente
- El resultado correcto debe ser:
Video: h264, yuv420p, 1920x1080, 30fps+Audio: opus, 48000 Hz, stereo
Parte 3 — API endpoints completos de WHIP
3.1 GET /api/v3/whip
Lista todos los publishers activos en este momento.
Response 200:
[
{
"name": "mistream",
"published_at": "2026-03-14T02:51:36.799550551Z"
}
]
Usar con polling cada 5s para el panel de streams activos y el indicador de estado en Receive mode.
3.2 GET /api/v3/whip/url ⭐ nuevo
Devuelve la URL base del servidor WHIP y toda la info que la UI necesita para el panel "WHIP Server".
Response 200:
{
"base_publish_url": "http://192.168.1.15:8555/whip/",
"base_sdp_url": "http://localhost:8555/whip/",
"has_token": false,
"example_obs_url": "http://192.168.1.15:8555/whip/<stream-key>",
"input_address_template": "{whip:name=<stream-key>}"
}
base_publish_url→ mostrar en el panel WHIP Server; el usuario agrega su stream key al finalhas_token→ si estrue, mostrar aviso de token y el campo para incluirlo en la URLexample_obs_url→ texto de ayuda con placeholder visualinput_address_template→ usar comoinput.addressal crear un proceso con WHIP como fuente
3.3 GET /api/v3/whip/:name/url ⭐ nuevo
Devuelve la URL completa de publicación para un stream key específico. Este es el endpoint principal para generar la URL que el usuario copia en OBS.
Ejemplo: GET /api/v3/whip/mistream/url
Response 200:
{
"publish_url": "http://192.168.1.15:8555/whip/mistream",
"sdp_url": "http://localhost:8555/whip/mistream/sdp",
"stream_key": "mistream"
}
publish_url→ campo read-only en la UI con botón Copiar. Es exactamente la URL que se pega en OBS → Configuración → Transmisión → Servidor.sdp_url→ URL interna para FFmpeg (referencia, no se muestra al usuario en general).stream_key→ confirmación del key procesado.
Flujo de uso en la UI:
- Usuario escribe el stream key en el campo de texto
- UI llama
GET /api/v3/whip/<stream_key>/url - UI muestra
publish_urlen campo read-only con botón Copiar - Usuario copia y pega en OBS
3.4 GET /api/v3/config
Leer configuración del servidor WHIP para el panel de settings.
Campos relevantes en la respuesta:
{
"config": {
"host": {
"name": ["192.168.1.15"],
"auto": true
},
"whip": {
"enable": true,
"address": ":8555",
"token": "",
"rtsp_address": ":8554"
}
}
}
3.5 PATCH /api/v3/config (o PUT)
Modificar configuración WHIP desde el panel de settings.
Body:
{
"whip": {
"enable": true,
"address": ":8555",
"token": "mi-token-secreto"
}
}
3.6 POST /api/v3/process
Crear un proceso con WHIP como fuente de entrada.
Body:
{
"id": "mi-proceso",
"reference": "mi-proceso",
"input": [
{
"id": "in",
"address": "{whip:name=mistream}",
"options": []
}
],
"output": [
{
"id": "out",
"address": "rtmp://...",
"options": ["-c", "copy", "-f", "flv"]
}
],
"options": ["-loglevel", "level+info"]
}
El Core expande
{whip:name=mistream}→http://localhost:8555/whip/mistream/sdpe inyecta automáticamente-protocol_whitelist file,crypto,http,rtp,udp,tcp.
Si la UI no usa el placeholder {whip}, usar directamente:
{
"address": "http://localhost:8555/whip/mistream/sdp",
"options": ["-protocol_whitelist", "file,crypto,http,rtp,udp,tcp"]
}
3.7 GET /api/v3/process/:id/probe
Obtener información del stream (resolución, codec, fps) una vez el proceso existe.
Response 200 (cuando OBS está transmitiendo):
{
"streams": [
{
"type": "video",
"codec": "h264",
"width": 1920,
"height": 1080,
"fps": 30,
"pix_fmt": "yuv420p"
},
{
"type": "audio",
"codec": "opus",
"sampling_hz": 48000,
"layout": "stereo",
"channels": 2
}
]
}
Tabla resumen
| Endpoint | Método | Cuándo usarlo |
|---|---|---|
/api/v3/whip |
GET | Polling streams activos (cada 5s) |
/api/v3/whip/url |
GET | Panel WHIP Server — info base del servidor |
/api/v3/whip/:name/url |
GET | Generar URL para OBS dado un stream key |
/api/v3/config |
GET | Leer estado enable/disable, puerto, token |
/api/v3/config |
PATCH | Cambiar configuración desde settings |
/api/v3/process |
POST | Crear proceso con WHIP como input |
/api/v3/process/:id/probe |
GET | Detectar resolución/codec del stream |
Parte 4 — Instrucciones para el usuario en la UI
En el panel WHIP Server
Cómo transmitir desde OBS:
1. Abrí OBS → Configuración → Transmisión
2. Servicio: Personalizado
3. Servidor: http://<host>:<puerto>/whip/<tu-stream-key>
4. Clave de retransmisión: (dejar vacío)
5. Hacé clic en "Iniciar transmisión"
En el Receive mode WHIP
Ingresá tu stream key, copiá la URL y configurala en OBS.
El proceso comenzará a recibir video cuando OBS empiece a transmitir.
Parte 5 — Recuperación de estado tras reinicio del Core
Cuando el Core se reinicia, la UI debe re-leer el estado WHIP desde la API. La UI no debe cachear los valores localmente — siempre debe obtenerlos del Core en el momento de montar el componente.
5.1 Secuencia de inicialización del componente WHIP
Al montar cualquier componente/página que muestre datos WHIP, ejecutar en este orden:
async function load() {
// 1. Leer config del servidor
const { data } = await api.get('/api/v3/config')
const whip = data.config.whip ?? { enable: false, address: ':8555', token: '' }
// 2. Extraer puerto sin el prefijo ':'
const port = whip.address.replace(/^:/, '') || '8555'
// 3. Inicializar estado del formulario
configValues.whip = {
enable: whip.enable,
address: port, // mostrar "8555", no ":8555"
token: whip.token,
rtspAddress: (whip.rtsp_address ?? '').replace(/^:/, ''), // "8554" o ""
}
// 4. Obtener la URL base de publicación (host público)
const urlInfo = await api.get('/api/v3/whip/url')
state.basePublishUrl = urlInfo.data.base_publish_url // "http://189.x.x.x:8555/whip/"
state.hasToken = urlInfo.data.has_token
// 5. Listar publishers activos
const channels = await api.get('/api/v3/whip')
state.activeChannels = channels.data // [{ name, published_at }]
}
5.2 Detectar que el Core se reinició
El Core no emite eventos de reconexión, pero la UI puede detectarlo por:
- Polling de
/api/v3/whip— si la llamada falla (network error / 502) y luego vuelve a responder, el Core se reinició. Llamarload()nuevamente. updated_aten/api/v3/config— si cambia respecto al valor previo cacheado, el Core fue reiniciado y la config debe recargarse.
// Ejemplo: detectar reinicio por cambio en updated_at
let lastUpdatedAt = null
setInterval(async () => {
try {
const { data } = await api.get('/api/v3/config')
if (lastUpdatedAt && data.updated_at !== lastUpdatedAt) {
await load() // Core reinició, recargar todo
}
lastUpdatedAt = data.updated_at
} catch {
// Core no disponible temporalmente, reintentar en próximo ciclo
}
}, 10000) // cada 10s
5.3 Comportamiento esperado en el panel WHIP Server tras reinicio
| Estado | Qué hace la UI |
|---|---|
| Core acaba de arrancar, config.json existe | load() lee whip.token = 'heavy666' del disco — se muestra correctamente ✅ |
| Core arranca sin config.json (primera vez) | whip no existe en respuesta → usar defaults { enable: false, address: '8555', token: '' } |
| Core no responde (arrancando) | Mostrar spinner/skeleton, reintentar cada 2s hasta que responda |
| Core responde pero WHIP disabled | Mostrar badge "Disabled", ocultar la URL de publicación |
5.4 Flujo de guardado (handleSave)
async function handleSave() {
// Leer config completa actual para no pisar otros campos
const { data } = await api.get('/api/v3/config')
const fullConfig = data.config
// Aplicar cambios WHIP (restaurar ':' en address)
fullConfig.whip = {
enable: configValues.whip.enable,
address: ':' + configValues.whip.address, // "8555" → ":8555"
token: configValues.whip.token,
rtsp_address: configValues.whip.rtspAddress ? ':' + configValues.whip.rtspAddress : '', // "" o ":8554"
}
// Enviar config completa
await api.put('/api/v3/config', fullConfig)
// Recargar URL pública por si cambió el puerto
const urlInfo = await api.get('/api/v3/whip/url')
state.basePublishUrl = urlInfo.data.base_publish_url
}
Importante: usar siempre
PUT /api/v3/configcon el objeto completo. El Core no soporta PATCH parcial — si se envía solo{ whip: {...} }, los demás campos se resetean a defaults.
Notas técnicas para el desarrollador de la UI
-
Compatibilidad de OBS: OBS 30+ soporta WHIP nativo. En Configuración → Transmisión, seleccionar "Servicio: Personalizado" y pegar la URL completa.
-
ICE / WebRTC: El Core maneja ICE + DTLS-SRTP internamente. La UI no necesita gestionar nada de WebRTC, solo mostrar la URL HTTP.
-
Protocolo WHIP: El cliente (OBS) hace un
POST /whip/<key>con SDP offer → el Core responde con SDP answer → ICE handshake → fluye RTP → el Core lo relay internamente a FFmpeg. -
protocol_whitelist: Es obligatorio cuando FFmpeg lee la URL/whip/<key>/sdp. El Core lo inyecta automáticamente cuando detecta la URL en el config del proceso, pero si la UI construye el comando FFmpeg directamente debe asegurarse de incluirlo. -
Token de autenticación: Si
config.whip.tokenestá configurado, la URL de publicación para OBS debe incluirlo como query param:?token=<value>. El OBS lo enviará en cada request al WHIP endpoint. -
Puertos: Por defecto el WHIP server escucha en
:8555(HTTP, sin TLS). El Core HTTP principal escucha en:8080. Son servidores independientes. -
Múltiples publishers: El servidor WHIP soporta múltiples stream keys simultáneos. Cada stream key es independiente y genera su propio relay interno.
Parte 6 — WHEP: reproducción en browser sin FFmpeg (nueva funcionalidad)
WHEP (WebRTC HTTP Egress Protocol) es el complemento de WHIP. Permite que un browser reciba el stream de OBS directamente con latencia ~300ms, sin transcoding ni FFmpeg. El Core actúa como SFU: recibe RTP de OBS y lo reenvía vía DTLS-SRTP a cada suscriptor.
6.1 Cambios en las APIs existentes
GET /api/v3/whip — campo nuevo: subscribers
[
{
"name": "b89d39bb-5321-46f3-8d89-54a03150205d",
"published_at": "2026-03-14T20:09:09.87237418Z",
"subscribers": 2
}
]
Usar subscribers para mostrar el contador de viewers en tiempo real en el panel WHIP Server.
GET /api/v3/whip/:name/url — campo nuevo: whep_url
{
"publish_url": "http://192.168.1.15:8555/whip/mystream",
"sdp_url": "http://localhost:8555/whip/mystream/sdp",
"stream_key": "mystream",
"whep_url": "http://192.168.1.15:8555/whip/mystream/whep"
}
GET /api/v3/whip/url — campo nuevo: base_whep_url
{
"base_publish_url": "http://192.168.1.15:8555/whip/",
"base_sdp_url": "http://localhost:8555/whip/",
"base_whep_url": "http://192.168.1.15:8555/whip/",
"has_token": false,
"example_obs_url": "http://192.168.1.15:8555/whip/<stream-key>",
"input_address_template": "{whip:name=<stream-key>}"
}
La whep_url completa se construye como: base_whep_url + stream_key + "/whep".
6.2 Nuevo endpoint WHEP
POST /whip/:name/whep — suscribirse al stream (en puerto 8555, no 8080)
El browser envía un SDP offer; el Core responde con SDP answer + ICE candidates.
IMPORTANTE: Este endpoint vive en el servidor WHIP (puerto
:8555), no en la API REST de Core (:8080). Llamarlo directamente desde el browser:POST http://192.168.1.15:8555/whip/mystream/whep
Headers de request:
Content-Type: application/sdp
Authorization: Bearer <token> (solo si has_token = true)
Response 201 Created:
Content-Type: application/sdp
Location: /whip/mystream/whep/<subid>
Body: <SDP answer con ICE candidates>
Response 404 si no existe el stream key (canal no creado aún).
Response 401 si el token es requerido y no fue enviado.
DELETE /whip/:name/whep/:subid — cerrar suscripción
DELETE http://192.168.1.15:8555/whip/mystream/whep/<subid>
Donde <subid> es el último segmento del header Location devuelto en el POST.
6.3 Preview embed en el panel WHIP Server
Agregar un botón "Ver en vivo" / "Preview" en la tarjeta de cada stream activo. Al hacer clic:
async function startWHEPPreview(streamKey, whepBaseUrl) {
const pc = new RTCPeerConnection({ iceServers: [] })
pc.addTransceiver('video', { direction: 'recvonly' })
pc.addTransceiver('audio', { direction: 'recvonly' })
const offer = await pc.createOffer()
await pc.setLocalDescription(offer)
const whepUrl = `${whepBaseUrl}${streamKey}/whep`
const headers = { 'Content-Type': 'application/sdp' }
if (token) headers['Authorization'] = `Bearer ${token}`
const resp = await fetch(whepUrl, {
method: 'POST',
headers,
body: offer.sdp,
})
if (!resp.ok) {
throw new Error(`WHEP error ${resp.status}`)
}
const locationHeader = resp.headers.get('Location')
const answerSDP = await resp.text()
await pc.setRemoteDescription({ type: 'answer', sdp: answerSDP })
// Mostrar video en el elemento <video>
pc.ontrack = (e) => {
if (e.track.kind === 'video') {
videoElement.srcObject = e.streams[0]
}
}
// Guardar para poder cerrar después
return { pc, locationHeader }
}
async function stopWHEPPreview(pc, locationHeader, whipServerBase) {
pc.close()
if (locationHeader) {
await fetch(whipServerBase + locationHeader, { method: 'DELETE' })
}
}
Elemento HTML necesario:
<video id="whep-preview" autoplay playsinline muted controls></video>
Usar
mutedinicialmente para evitar bloqueo de autoplay en Chrome/Firefox.
Mostrar un botón de unmute para que el usuario active el audio voluntariamente.
6.4 CORS — el browser puede llamar directamente
El servidor WHIP responde con los headers CORS correctos en todos los endpoints:
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Methods: POST, DELETE, OPTIONS
Access-Control-Expose-Headers: Location
No se necesita proxy en la UI. El fetch() al puerto 8555 funciona desde el browser sin problemas de CORS.
6.5 Indicadores de estado en el panel
| Situación | Qué mostrar |
|---|---|
OBS publicando, subscribers: 0 |
Badge "En vivo" + botón "Preview" |
OBS publicando, subscribers: N |
Badge "En vivo" + "N viewers" + botón "Preview activo" |
| OBS no publicando | Badge "Sin señal" — ocultar botón Preview |
| WHEP no soportado (token WebRTC no disponible) | No mostrar botón Preview |
Actualizar el contador de subscribers con el mismo polling de 5s de GET /api/v3/whip.
6.6 Tabla resumen de todos los endpoints WHEP/WHIP activos
| Endpoint | Puerto | Método | Descripción |
|---|---|---|---|
/api/v3/whip |
8080 | GET | Lista publishers + contador subscribers |
/api/v3/whip/url |
8080 | GET | URL base del servidor + base_whep_url |
/api/v3/whip/:name/url |
8080 | GET | URLs completas: publish, sdp, whep |
/whip/:name/whep |
8555 | POST | Suscribirse vía WHEP (desde browser) |
/whip/:name/whep/:subid |
8555 | DELETE | Cerrar suscripción WHEP |
Parte 7 — Multi-egress con {whip-rtsp:name=X} (relay RTSP interno)
7.1 Problema que resuelve
El placeholder {whip:name=X} (Parte 2) funciona bien para un solo proceso FFmpeg que consume el stream WHIP. Sin embargo, si se crean dos o más procesos que usan {whip:name=X} con el mismo stream key (por ejemplo, re-emitir a YouTube + Facebook + Twitch desde un solo OBS), solo uno puede leer el relay UDP loopback simultáneamente.
El relay RTSP interno resuelve esto: expone el stream como un servidor RTSP TCP al que pueden conectarse N clientes FFmpeg en paralelo, cada uno recibiendo una copia independiente.
OBS → WHIP → Core
├── {whip:name=X} → proceso: HLS/memfs (1 consumidor)
└── RTSP relay :8554
├── {whip-rtsp:name=X} → proceso: YouTube
├── {whip-rtsp:name=X} → proceso: Facebook
└── {whip-rtsp:name=X} → proceso: Twitch
7.2 Requisito en config
El relay RTSP debe estar habilitado en la configuración del Core:
"whip": {
"enable": true,
"address": ":8555",
"token": "",
"rtsp_address": ":8554"
}
Si rtsp_address está vacío, el relay no arranca y {whip-rtsp} no funciona.
7.3 Placeholder de proceso
En el input.address de cualquier proceso de egress:
{whip-rtsp:name=<stream_key>}
El Core lo expande a rtsp://127.0.0.1:8554/live/<stream_key> e inyecta automáticamente -rtsp_transport tcp en las input options (para forzar RTP/AVP/TCP interleaved, el único modo soportado por el relay interno).
Alternativa sin placeholder (si la UI no usa el mecanismo de templates):
{
"address": "rtsp://127.0.0.1:8554/live/mistream",
"options": ["-rtsp_transport", "tcp"]
}
En este caso la UI debe incluir -rtsp_transport tcp manualmente.
7.4 Diferencias con {whip}
{whip:name=X} |
{whip-rtsp:name=X} |
|
|---|---|---|
| Protocolo | HTTP → SDP → UDP RTP | RTSP/TCP (interleaved) |
| Consumidores simultáneos | 1 | N (ilimitado) |
Requiere rtsp_address en config |
No | Sí |
| Auto-inyección de opciones FFmpeg | -protocol_whitelist … |
-rtsp_transport tcp |
| Latencia adicional | Ninguna | Ninguna (~igual) |
| Uso típico | Proceso principal de grabación/HLS | Procesos de re-streaming a plataformas |
7.5 UI en el wizard de proceso — modo "WHIP Multi-Egress"
Cuando el usuario selecciona WHIP como fuente de entrada en un proceso, mostrar un selector de modo:
┌─────────────────────────────────────────────────────────────┐
│ Modo de acceso al stream WHIP │
│ │
│ ○ Directo — Un solo proceso lee el stream (recomendado │
│ para grabación/HLS mientras OBS transmite) │
│ │
│ ○ Relay RTSP — Múltiples procesos pueden leer │
│ simultáneamente (YouTube + Facebook + …) │
│ Requiere "Puerto relay RTSP" configurado │
└─────────────────────────────────────────────────────────────┘
Modo Directo → input.address = "{whip:name=<stream_key>}"
Modo Relay RTSP → input.address = "{whip-rtsp:name=<stream_key>}"
Si el modo Relay RTSP está seleccionado y config.whip.rtsp_address está vacío, mostrar un aviso:
⚠️ El relay RTSP no está configurado. Activalo en Configuración → WHIP Server → Puerto relay RTSP.
7.6 Creación de proceso via API
Payload para proceso de re-streaming a YouTube usando relay RTSP:
{
"id": "obs-to-youtube",
"reference": "obs-to-youtube",
"input": [
{
"id": "in",
"address": "{whip-rtsp:name=mistream}",
"options": []
}
],
"output": [
{
"id": "out",
"address": "rtmp://a.rtmp.youtube.com/live2/STREAM_KEY",
"options": ["-c", "copy", "-f", "flv"]
}
],
"options": ["-loglevel", "level+info"]
}
El Core expande
{whip-rtsp:name=mistream}→rtsp://127.0.0.1:8554/live/mistreame inyecta-rtsp_transport tcpautomáticamente en las input options.
Tres procesos de egress simultáneos (YouTube + Facebook + Twitch):
// proceso 1
{ "input": [{ "address": "{whip-rtsp:name=mistream}" }], "output": [{ "address": "rtmp://youtube..." }] }
// proceso 2
{ "input": [{ "address": "{whip-rtsp:name=mistream}" }], "output": [{ "address": "rtmp://facebook..." }] }
// proceso 3
{ "input": [{ "address": "{whip-rtsp:name=mistream}" }], "output": [{ "address": "rtmp://twitch..." }] }
Los tres leen del relay RTSP independientemente; OBS solo transmite una vez.
7.7 Tabla resumen de endpoints actualizada
| Endpoint | Puerto | Método | Descripción |
|---|---|---|---|
/api/v3/whip |
8080 | GET | Lista publishers + subscribers |
/api/v3/whip/url |
8080 | GET | URL base + base_whep_url |
/api/v3/whip/:name/url |
8080 | GET | URLs completas: publish, sdp, whep |
/whip/:name |
8555 | POST | Publicar stream WHIP (OBS/clientes) |
/whip/:name/sdp |
8555 | GET | SDP para FFmpeg (modo directo) |
/whip/:name/whep |
8555 | POST | Suscribirse vía WHEP (browser) |
/whip/:name/whep/:subid |
8555 | DELETE | Cerrar suscripción WHEP |
rtsp://localhost/live/:name |
8554 | — | Relay RTSP multi-consumer (TCP only) |