Ir al contenido

Streaming por LAN

Autentícate directamente con cámaras y gateways de audio de Rhombus en tu red local para video en vivo, grabaciones y audio bidireccional.

En esta página

Descripción general

Cuando tu cliente está en la misma red que un dispositivo Rhombus, puede obtener video en vivo, grabaciones y audio directamente del dispositivo en lugar de hacerlo a través de la nube de Rhombus. Obtienes menor latencia y no consumes ancho de banda de internet.

Advertencia

Los dispositivos de tu LAN solo aceptan tokens federados. Es una decisión deliberada: una solicitud LAN va directo al dispositivo en tu red local, así que el dispositivo acepta un token de corta duración, idealmente limitado a ese único dispositivo, y nunca una clave de API ni un access token de OAuth de larga duración. Usa tu clave de API en tu servidor para generar un token federado de corta duración y luego presenta el token federado al dispositivo.

Esta guía cubre:

  • En qué se diferencian los API tokens y los tokens federados, y cuál usar en cada caso
  • Cómo generar un token federado y encontrar los URI LAN de un dispositivo
  • Cómo autenticar solicitudes LAN con encabezados o parámetros de consulta
  • H.264 en vivo y audio bidireccional por WebSocket, además de DASH/HLS para video en vivo y grabado
  • Limitaciones de los navegadores y solución de problemas

API tokens vs tokens federados

Son dos credenciales distintas con funciones distintas.

Token de APIToken federado
Qué esEl secreto de larga duración de tu integración, creado en la Rhombus ConsoleUn token de corta duración que generas con un token de API mediante POST /api/org/generateFederatedSessionToken
DuraciónHasta que lo elimines, o hasta su vencimiento si tiene unoEl durationSec que solicites (una hora de forma predeterminada, 24 horas como máximo); también deja de funcionar cuando lo revocas o cuando se revoca la clave de API que lo generó
PermisosEl rol asignado al token de APIEl mismo rol que el token de API que lo generó, opcionalmente limitado a un dispositivo
Encabezadosx-auth-scheme: api-token y x-auth-apikeyx-auth-scheme: federated-token y x-auth-ft
Parámetros de consultaNo soportados?x-auth-scheme=federated-token&x-auth-ft=TOKEN (la API en la nube, los medios en la nube y los dispositivos LAN)
API en la nube de RhombusSíSí, con encabezados o parámetros de consulta (no los tokens limitados a un dispositivo)
Medios en la nube de Rhombus (URL de streams WAN)Sí, con encabezados (clientes del lado del servidor)Sí, con encabezados o parámetros de consulta
Dispositivos de tu LANNo, nunca se aceptanSí, con encabezados o parámetros de consulta. Desde un navegador, solo funcionan las URL de WebSocket con parámetros de consulta (consulta Aplicaciones de navegador)
Dónde puede estarSolo en tu servidorNavegadores, reproductores y clientes LAN

Un token federado es una credencial al portador: cualquiera que lo tenga puede actuar con su rol. Para streaming por LAN, genera tokens limitados a un dispositivo con deviceUUid. Un token limitado a un dispositivo solo puede obtener los medios de ese dispositivo, y la API en la nube lo rechaza. Además, genéralo con un token de API cuyo rol no sea más amplio de lo que el espectador necesita, y mantén durationSec corto.

Requisitos previos

Implementación

  1. Genera un token federado en tu servidor

    Llama a generateFederatedSessionToken con tu clave de API. Tu clave de API nunca sale de tu servidor.

    import requests
    
    API_HEADERS = {
        "x-auth-scheme": "api-token",
        "x-auth-apikey": "YOUR_API_KEY",
        "Content-Type": "application/json",
    }
    
    response = requests.post(
        "https://api2.rhombussystems.com/api/org/generateFederatedSessionToken",
        headers=API_HEADERS,
        json={
            "durationSec": 3600,               # duración del token en segundos
            "deviceUUid": "YOUR_CAMERA_UUID",  # recomendado: solo los medios de este dispositivo
        },
    )
    response.raise_for_status()
    body = response.json()
    federated_token = body["federatedSessionToken"]
    expires_at_ms = body["expiresAtMs"]  # ms Unix; genera el siguiente token antes de ese momento

    Ejemplo de respuesta:

    json
    {
      "federatedSessionToken": "Q2gP7m1kTnWb3sXyZ4aJ8w",
      "expiresAtMs": 1791370800000
    }
    CampoNotas
    durationSecOpcional. La duración del token en segundos: 3600 (1 hora) si lo omites, 86400 (24 horas) como máximo (un valor mayor se reduce a ese, y expiresAtMs muestra la duración que obtuviste). Solicita solo la duración que necesita la sesión.
    deviceUUidOpcional, recomendado para LAN. Limita el token a los medios (LAN y WAN) de un dispositivo; el token no puede llamar a la API en la nube. Ten en cuenta las mayúsculas: deviceUUid.
    domainOpcional. El origen del navegador autorizado a usar el token, como origen absoluto, por ejemplo https://app.example.com. Las solicitudes a los hosts en la nube de Rhombus desde cualquier otro origen reciben 403 DOMAIN_NOT_ALLOWED. Para limitar un token LAN, usa deviceUUid y un durationSec corto.

    El expiresAtMs de la respuesta indica cuándo expira el token, en milisegundos Unix. Genera el siguiente token antes de ese momento.

    El token nunca puede hacer más que el token de API que lo generó. Si ese rol solo permite video en vivo, el dispositivo sirve streams en vivo y rechaza las grabaciones.

  2. Obtén los URI LAN del dispositivo

    En tu servidor, llama a POST /api/camera/getMediaUris con cameraUuid, o a /api/audiogateway/getMediaUris con gatewayUuid, usando tu clave de API. Un token federado limitado a un dispositivo no puede llamar a la API en la nube.

    Python
    response = requests.post(
        "https://api2.rhombussystems.com/api/camera/getMediaUris",
        headers=API_HEADERS,
        json={"cameraUuid": "YOUR_CAMERA_UUID"},
    )
    response.raise_for_status()
    uris = response.json()
    CampoQué esEjemplo
    lanLiveH264UrisH.264 en vivo por WebSocket (la menor latencia)wss://192-168-1-50.lan.rhombussystems.com:8000/DEVICE_UUID/ws
    lanLiveOpusUrisAudio bidireccional en vivo por WebSocketwss://192-168-1-50.lan.rhombussystems.com:8000/DEVICE_UUID/audio
    lanLiveMpdUris / lanLiveM3u8UrisDASH / HLS en vivohttps://192-168-1-50.lan.rhombussystems.com:8000/DEVICE_UUID/live/live.mpd
    lanVodMpdUrisTemplates / lanVodM3u8UrisTemplatesGrabaciones del almacenamiento propio del dispositivohttps://192-168-1-50.lan.rhombussystems.com:8000/DEVICE_UUID/store/{START_TIME}_{DURATION}/clip.mpd
    lanCheckUrlsVerificación de alcanzabilidadhttps://192-168-1-50.lan.rhombussystems.com:8000/DEVICE_UUID/
    • Cada campo es un arreglo porque un dispositivo puede tener más de una dirección LAN. Usa la primera que tu cliente pueda alcanzar.
    • En las plantillas VOD, reemplaza {START_TIME} por una marca de tiempo Unix en segundos y {DURATION} por una duración en segundos.
    • El nombre de host LAN es DNS público que resuelve a la IP privada del dispositivo, y el dispositivo sirve un certificado de confianza pública para él. Conéctate usando el nombre de host exactamente como se devuelve, no la IP directa, y mantén activada la verificación TLS normal. Los dispositivos de la región UE usan nombres de host .lan.eu.rhombussystems.com (Regiones de API).
  3. Presenta el token federado al dispositivo

    Envía el token federado en cada solicitud al dispositivo, incluido cada segmento DASH/HLS y el upgrade del WebSocket. Usa la forma que admita tu cliente:

    Para apps nativas, servidores, curl y ffmpeg:

    x-auth-scheme: federated-token
    x-auth-ft: YOUR_FEDERATED_TOKEN

    El dispositivo verifica el token con Rhombus: debe estar vigente, pertenecer a la organización del dispositivo y tener un rol que pueda ver este dispositivo. El dispositivo guarda en caché una verificación exitosa hasta por 60 segundos, así que un token puede seguir funcionando hasta un minuto después de expirar o de ser revocado. Cuando termine la sesión de un espectador, revoca su token con POST /api/org/revokeFederatedSessionToken (cuerpo {"federatedSessionToken": "..."}), y mantén durationSec corto.

Ejemplos de código

Manifiesto DASH en vivo

cURLbash
TOKEN="YOUR_FEDERATED_TOKEN"

curl -H "x-auth-scheme: federated-token" -H "x-auth-ft: $TOKEN" \
  "https://192-168-1-50.lan.rhombussystems.com:8000/YOUR_CAMERA_UUID/live/live.mpd"

Un token válido devuelve el manifiesto DASH (200). Un token ausente, expirado o no autorizado devuelve 401.

Grabaciones con ffmpeg

ffmpeg pasa -headers a cada solicitud de playlist y de segmento. Esto guarda 60 segundos de grabación a partir de 1767225600 (2026-01-01 00:00:00 UTC):

ffmpegbash
ffmpeg -headers $'x-auth-scheme: federated-token\r\nx-auth-ft: '"$TOKEN"$'\r\n' \
  -i "https://192-168-1-50.lan.rhombussystems.com:8000/YOUR_CAMERA_UUID/store/1767225600_60/clip.m3u8" \
  -c copy clip.mp4

H.264 en vivo por WebSocket

import asyncio
import json
from urllib.parse import urlencode

import requests
import websockets  # pip install websockets

API_HEADERS = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json",
}
CAMERA_UUID = "YOUR_CAMERA_UUID"


def mint_device_token(device_uuid: str) -> tuple[str, int]:
    response = requests.post(
        "https://api2.rhombussystems.com/api/org/generateFederatedSessionToken",
        headers=API_HEADERS,
        json={"durationSec": 3600, "deviceUUid": device_uuid},
    )
    response.raise_for_status()
    body = response.json()
    # Genera el siguiente token antes de expiresAtMs (milisegundos Unix)
    return body["federatedSessionToken"], body["expiresAtMs"]


def lan_h264_uri(camera_uuid: str) -> str:
    response = requests.post(
        "https://api2.rhombussystems.com/api/camera/getMediaUris",
        headers=API_HEADERS,
        json={"cameraUuid": camera_uuid},
    )
    response.raise_for_status()
    uris = response.json().get("lanLiveH264Uris") or []
    if not uris:
        raise SystemExit("La cámara no reportó ninguna dirección LAN")
    return uris[0]


async def watch(uri: str, token: str, max_frames: int = 30) -> None:
    auth = urlencode({"x-auth-scheme": "federated-token", "x-auth-ft": token})
    async with websockets.connect(f"{uri}?{auth}") as ws:
        frames = 0
        async for message in ws:
            if isinstance(message, str):  # primero llega un mensaje de texto init
                print("init:", json.loads(message))
                continue
            timestamp_ms, offset = None, 0
            while offset + 4 <= len(message):
                tlv_type = message[offset]
                length = int.from_bytes(message[offset + 1 : offset + 4], "big")
                value = message[offset + 4 : offset + 4 + length]
                offset += 4 + length
                if tlv_type == 0x02:
                    timestamp_ms = int.from_bytes(value, "big")
                elif tlv_type in (0x00, 0x01):  # los datos del frame siempre van al final
                    kind = "keyframe" if tlv_type == 0x00 else "delta"
                    print(f"{kind}: {len(value)} bytes at {timestamp_ms}")
                    frames += 1
                    break
            if frames >= max_frames:
                break


token, expires_at_ms = mint_device_token(CAMERA_UUID)
asyncio.run(watch(lan_h264_uri(CAMERA_UUID), token))

Ejemplo de salida del script de Python:

init: {'action': 'init', 'width': 1920, 'height': 1080, 'codec': 'h264', 'framerate': 15}
keyframe: 48213 bytes at 1791158465584
delta: 3120 bytes at 1791158465651

Cada mensaje binario es una secuencia de registros TLV (tipo de 1 byte, longitud de 3 bytes big-endian, valor) que termina con los datos del frame H.264. La tabla completa de tipos, incluidas las detecciones de IA incrustadas, está en Superposición de detección en tiempo real por LAN. Para un reproductor de navegador listo para usar, usa RhombusRealtimePlayer con connectionMode="lan" del React SDK.

Audio por WebSocket

El socket lanLiveOpusUris transporta audio en ambas direcciones y usa la misma autenticación con token federado.

Escuchar

Cada mensaje binario del dispositivo contiene tres registros TLV. El encabezado de cada registro tiene 1 bit (bandera de eco), 7 bits (tipo) y una longitud de 24 bits big-endian, seguido del valor.

TipoValor
0Cantidad de muestras (2 bytes, big-endian)
1Marca de tiempo Unix en milisegundos (8 bytes, big-endian)
4Un paquete Opus, 48 kHz mono. La bandera de eco se activa si el dispositivo estaba reproduciendo audio cuando se capturó.

Hablar

Para reproducir audio en el altavoz del dispositivo, envía mensajes binarios por el mismo socket. Cada registro es un tipo ASCII de 4 bytes, una longitud de 4 bytes big-endian y luego el contenido. Cada registro de audio contiene exactamente 20 ms de audio.

TipoContenido
opusUn paquete Opus, 48 kHz mono
pcm (con espacio al final)960 muestras PCM de 48 kHz mono, 16 bits con signo little-endian (longitud 1920)
ctrlMarca de tiempo Unix de 8 bytes big-endian en milisegundos para iniciar la reproducción. Envía 0 para reproducir cada paquete aunque llegue tarde, o -1 para descartar el audio atrasado y mantener el tiempo real.

De forma predeterminada, el dispositivo reproduce cada paquete Opus, incluso los atrasados, y descarta el PCM atrasado para mantener el tiempo real.

Aplicaciones de navegador

  • Los streams WebSocket (lanLiveH264Uris, lanLiveOpusUris) funcionan desde cualquier origen con el token en la cadena de consulta.
  • DASH y HLS por LAN (URL lanLive* y lanVod*) no se pueden cargar con JavaScript del navegador, como dash.js o hls.js, en tu propio dominio, porque los dispositivos solo permiten orígenes de Rhombus para solicitudes entre orígenes. Reproduce DASH/HLS por LAN desde una app nativa o un servidor, o usa el stream WebSocket en el navegador.
  • Nunca pongas una clave de API en código de navegador. Genera tokens federados en tu servidor y entrega solo esos al navegador.

Dispositivos con firmware anterior

Los dispositivos con firmware publicado antes de mediados de abril de 2026 solo leen el token desde una cookie. Si un cliente nativo debe admitirlos, envía también:

Cookie: RSESSIONID=RFT:YOUR_FEDERATED_TOKEN

El firmware actual también acepta esta cookie, así que un cliente nativo puede enviarla junto con los encabezados.

Solución de problemas

401 en todas las solicitudes

Ningún token llegó al dispositivo, x-auth-scheme no es exactamente federated-token, o enviaste una clave de API en lugar de un token federado. Los dispositivos nunca aceptan claves de API.

401 después de funcionar un tiempo

El token federado expiró, o se revocó (directamente, o porque se eliminó la clave de API que lo generó). Genera uno nuevo antes del expiresAtMs de la respuesta de generación.

401 en un dispositivo, bien en los demás

El token se generó con deviceUUid para otro dispositivo, o su rol no puede ver este dispositivo.

El video en vivo funciona, las grabaciones se rechazan

El rol del token, heredado de la clave de API que lo generó, solo permite video en vivo.

La conexión se cierra sin respuesta

El dispositivo no pudo alcanzar la nube de Rhombus para verificar el token.

Funciona con curl, falla en un navegador

Los navegadores bloquean las solicitudes DASH/HLS entre orígenes hacia el dispositivo. Consulta Aplicaciones de navegador.

Errores de TLS o de nombre de host

Usa el nombre de host LAN de getMediaUris, no la dirección IP directa.

Próximos pasos