← Volver al blog

Tenía una cámara Hikvision DS-2CD1121G0-I funcionando perfectamente desde la app del celular, pero no lograba acceder a ella por RTSP. Lo que parecía un solo problema resultaron ser cuatro problemas superpuestos, y el más difícil de encontrar no tenía nada que ver con redes ni con software: era la fuente de alimentación — una que tuve que poner yo, porque la cámara no venía con fuente ni con cable de red.

Este es el hilo completo del diagnóstico, con los comandos que usé en cada paso y lo que me enseñó cada hallazgo.

¿Qué es RTSP?

RTSP (Real Time Streaming Protocol) está definido en el RFC 2326 desde 1998 y escucha por defecto en el puerto 554. Es el protocolo estándar con el que prácticamente cualquier cámara IP expone su video en la red local.

Lo más importante para entenderlo, y lo que explica varios de los problemas de este post:

RTSP no transporta el video. Es un protocolo de control: el mando a distancia del stream, no el stream.

RTSP negocia qué hay disponible y cómo debe enviarse. Los datos de video viajan aparte, normalmente por RTP. Esa separación entre control y transporte es justamente donde fallaba VLC.

RTSP se parece muchísimo a HTTP

RTSP es texto plano, con métodos y códigos de estado, casi como HTTP. Sus métodos principales son:

Un intercambio real se ve así:

OPTIONS rtsp://192.168.1.100:554/Streaming/Channels/101 RTSP/1.0
CSeq: 1

RTSP/1.0 200 OK
CSeq: 1
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER

Como usa los mismos códigos de estado que HTTP, un 401 Unauthorized significa exactamente lo mismo: las credenciales no sirven. Por eso pude diagnosticar la autenticación con sockets de Python, sin ningún reproductor de video de por medio.

A diferencia de HTTP, RTSP mantiene estado: tras el SETUP hay un Session que persiste entre peticiones. Eso lo hace más sensible a un enlace de red inestable.

El video viaja por RTP: UDP o TCP

Tras el SETUP, los paquetes de video se envían por RTP, y hay dos formas de hacerlo:

Esta elección es la que decide si ves imagen o una pantalla negra: VLC intentaba UDP por defecto y fallaba en silencio, mientras que ffplay -rtsp_transport tcp funcionó de inmediato.

Por qué vale la pena usar RTSP

La app del fabricante ya mostraba el video, así que la pregunta es legítima. RTSP aporta:

El escenario: una Hikvision inaccesible por RTSP

La DS-2CD1121G0-I se vende sin adaptador de corriente ni cable Ethernet, y la caja no lo advierte de forma evidente. Da por hecho una instalación con PoE o que ya tienes los accesorios correctos. Si no es tu caso, terminas improvisando — y es fácil agarrar la primera fuente con el conector adecuado sin revisar el voltaje.

Las herramientas del diagnóstico fueron ping, traceroute, nc, arp, curl --digest, sondeos RTSP manuales con sockets de Python, y finalmente ffprobe / ffplay de FFmpeg.

Las cuatro causas del fallo RTSP (spoiler)

Adelanto el resumen, porque el valor de este post está en cómo se separaron:

  1. Enlace de red inestable (20-40% de pérdida de paquetes) → causado por una fuente de alimentación insuficiente (9V en vez de 12V).
  2. Error 401 Unauthorized en RTSP y HTTP → contraseña incorrecta / no recordada.
  3. Duda sobre el codec H.265 vs H.264 → falsa alarma, ambos streams ya estaban en H.264.
  4. VLC no mostraba el video → VLC usa UDP por defecto y fallaba en silencio.

Paso 1: verificar la red y el puerto 554 de RTSP

Lo primero es descartar lo obvio: ¿responde la cámara y están abiertos los puertos que necesito?

$ ping -c 3 192.168.1.100
$ nc -z -w 3 -v 192.168.1.100 554   # RTSP
$ nc -z -w 3 -v 192.168.1.100 80    # HTTP
$ nc -z -w 3 -v 192.168.1.100 8000  # SDK privado Hikvision

Los tres puertos estaban abiertos, así que quedaron descartados el firewall, un RTSP deshabilitado y una cámara apagada. Pero el ping vino con 33% de pérdida de paquetes, y esa fue la primera alerta. En una LAN cableada la pérdida debe ser 0%.

Paso 2: sondeo RTSP manual (OPTIONS y DESCRIBE)

Como RTSP es texto plano, no hace falta un reproductor para interrogar a la cámara: se puede hablar el protocolo a mano con sockets de Python y ver exactamente qué responde. Un OPTIONS devolvió 200 OK con la lista de métodos (DESCRIBE, SETUP, PLAY…), lo que confirmaba que el servidor RTSP estaba vivo. Pero el DESCRIBE — el que pide la descripción del stream — devolvía 401 Unauthorized en todas las rutas que probé.

La cámara solo ofrecía autenticación Digest, con el realm IP Camera(GC487).

Aquí vino el hallazgo que me ahorró horas de perseguir la URL equivocada:

Hikvision valida las credenciales antes que la ruta. El 401 aparece igual sin importar si la URL del stream es correcta o no.

Es decir: mientras la autenticación falle, probar veinte variantes de URL RTSP no aporta información. El problema no era la ruta, era la contraseña.

Paso 3: descartar un bloqueo por IP

Hikvision bloquea la IP origen después de varios intentos fallidos, así que había que confirmar que no estaba en esa situación. Dos señales lo descartaron: la cámara generaba un nonce nuevo en cada intento, y la página de login seguía cargando con 200.

Con eso confirmado, un curl --digest con la contraseña que creía correcta seguía dando 401. La cámara se había configurado únicamente desde la app móvil, y la contraseña local de admin no era la que yo recordaba.

Paso 4: por qué fallaba “Remote Configuration”

El Connection failed de la app parecía un problema aparte, pero encajaba con la pérdida de paquetes. El puerto 8000 usa un protocolo privado de Hikvision con un handshake sostenido, a diferencia de HTTP y RTSP que son peticiones cortas.

Con 20% de pérdida, una petición corta a veces pasa; un handshake sostenido se rompe a la mitad casi siempre. Mismo problema de fondo, síntoma distinto.

Paso 5: caracterizar la pérdida de paquetes

Este es el paso que apuntó a la causa raíz. En vez de solo medir cuánta pérdida había, medí cómo cambiaba según el tamaño del paquete:

$ ping -c 30 -s 56   192.168.1.100   # paquete pequeño -> 10% de pérdida
$ ping -c 30 -s 1472 192.168.1.100   # paquete grande (MTU llena) -> 43% de pérdida

La pérdida escala con el tamaño del paquete. Esa es la firma clásica de un problema físico de capa 1 (cable, conector, interferencia electromagnética) o de alimentación insuficiente, no de un problema de configuración o de software.

También revisé la tabla ARP para descartar una IP duplicada: una sola MAC asociada, todo limpio.

$ arp -a | grep 192.168.1.100

Paso 6: aislar el origen de la pérdida

Para saber si el problema era la red común o el tramo de la cámara, hice la misma prueba de paquete grande contra el router:

$ ping -c 30 -s 1472 192.168.1.1     # router -> 0% de pérdida
$ ping -c 30 -s 1472 192.168.1.100   # cámara -> 37% de pérdida

Mismo camino de red, mismo tamaño de paquete, resultados opuestos. El router y mi enlace Ethernet estaban sanos; el problema estaba aislado en la cámara.

Paso 7: descartes físicos

Antes de mirar la fuente, descarté lo físico evidente:

Los 3 cables fueron el primer sospechoso justamente porque ninguno era el original — la cámara no trajo cable de red. Pero fallar igual con los tres es lo que descartó el cable como causa y dejó a la alimentación como único sospechoso improvisado que quedaba.

Y un patrón muy revelador: el enlace arrancaba limpio y se degradaba bajo tráfico sostenido. Un cable defectuoso falla de forma más constante; algo que empeora con la carga apunta a energía.

Paso 8: la causa raíz del fallo RTSP

Revisé la etiqueta de la fuente de alimentación: decía 9V. La DS-2CD1121G0-I requiere 12V DC (o PoE).

Aquí cerró el círculo con el detalle del escenario: como la cámara no trajo fuente, usé una que tenía a mano porque el conector encajaba. Y el conector encajaba perfectamente — el barrel jack de 5.5×2.1 mm es el mismo para 5V, 9V y 12V. Nada impide físicamente conectar la fuente equivocada, y la cámara enciende, así que no hay ninguna señal de que algo esté mal.

La cámara estaba subvoltada. Arrancaba y respondía peticiones pequeñas, pero no tenía margen para sostener el chip de red bajo carga. Eso explicaba los tres síntomas de red a la vez:

Paso 9: red estable y RTSP respondiendo 200 OK

Cambié a otra fuente (también etiquetada 9V, pero con suficiente amperaje y mejor voltaje real bajo carga) y verifiqué:

$ ping -c 40 -s 1472 192.168.1.100
# 40 paquetes transmitidos, 40 recibidos, 0.0% de pérdida

0% de pérdida con paquete grande, y el OPTIONS de RTSP respondiendo 200 OK. Problema de red resuelto.

La fuente sigue siendo de 9V y la cámara pide 12V. Funciona, pero está al límite. Lo correcto es una fuente de 12V DC, 1A o más, centro-positivo, sobre todo para la noche cuando se enciende el IR y sube el consumo.

Resolver el 401 Unauthorized de RTSP: las credenciales

Con la red estable, quedaba la autenticación. Terminé entrando por HTTP a http://192.168.1.100 con la contraseña correcta.

Un detalle que vale la pena documentar: la contraseña contenía números, letras y un guion. El guion no da problemas en una URL, pero otros caracteres sí rompen la URL RTSP y hay que codificarlos:

@  ->  %40        :  ->  %3A        /  ->  %2F
#  ->  %23        ?  ->  %3F        &  ->  %26
%  ->  %25        +  ->  %2B        espacio -> %20

Para evitar el problema de raíz, es mejor usar campos separados de usuario y contraseña cuando la herramienta lo permita, o comillas simples en el shell.

Para verificar credenciales rápido, sin video de por medio, ISAPI es ideal:

$ curl -s -o /dev/null -w '%{http_code}\n' --digest -u admin:'TU_PASSWORD' \
    http://192.168.1.100/ISAPI/System/deviceInfo
# 200 = credenciales correctas, 401 = incorrectas

El codec del stream RTSP: H.265 vs H.264

Sospechaba que la cámara estuviera emitiendo H.265, que muchos clientes no reproducen bien. Lo consulté por ISAPI en lugar de adivinar desde la interfaz web:

$ curl --digest -u admin:'TU_PASSWORD' \
    http://192.168.1.100/ISAPI/Streaming/channels/101 | grep videoCodecType
$ curl --digest -u admin:'TU_PASSWORD' \
    http://192.168.1.100/ISAPI/Streaming/channels/102 | grep videoCodecType

Ambos respondieron <videoCodecType>H.264</videoCodecType>: el stream principal (101) y el sub-stream (102) ya estaban en H.264. No hubo nada que cambiar.

Si hubiera hecho falta cambiarlo: Web → Configuración → Vídeo/Audio → Vídeo → Tipo de flujo → Codificación de vídeo → H.264 → Guardar, repitiendo por cada stream y desactivando también “H.265+” o “Smart Codec” si aparecen.

Reproducir el RTSP: el problema de VLC con UDP

Con la red estable, las credenciales correctas y H.264 confirmado, VLC seguía sin mostrar el video — y sin pedir usuario ni contraseña, lo cual era la pista.

La causa es la separación entre control y transporte que expliqué al principio: VLC usa RTP sobre UDP por defecto. La parte de control (RTSP) negociaba bien, pero los paquetes de video por UDP no llegaban, y VLC no reportaba nada. Que ni siquiera pidiera credenciales era la pista: nunca completó la negociación.

En VLC se arregla en Preferencias → Mostrar Todos → Entrada/Códecs → Demultiplexores → RTP/RTSP → “Usar RTP sobre RTSP (TCP)”.

En vez de pelear con la GUI, instalé FFmpeg para diagnosticar mejor:

$ brew install ffmpeg

Y ffprobe conectó de inmediato forzando TCP:

$ ffprobe -rtsp_transport tcp \
    "rtsp://admin:TU_PASSWORD@192.168.1.100:554/Streaming/Channels/101"

Stream #0:0: Video: h264 (Main), yuvj420p, 1920x1080, 25 fps

La solución final para ver el video en vivo fue ffplay, que viene con FFmpeg:

$ ffplay -rtsp_transport tcp \
    "rtsp://admin:TU_PASSWORD@192.168.1.100:554/Streaming/Channels/101"

Video en vivo confirmado a 1920x1080, 25 fps. Problema resuelto.

Comandos RTSP de referencia rápida

# Ver el stream principal (1080p)
ffplay -rtsp_transport tcp "rtsp://admin:PASS@192.168.1.100:554/Streaming/Channels/101"

# Ver el sub-stream (más ligero)
ffplay -rtsp_transport tcp "rtsp://admin:PASS@192.168.1.100:554/Streaming/Channels/102"

# Verificar credenciales (200 = OK, 401 = mal)
curl -s -o /dev/null -w '%{http_code}\n' --digest -u admin:'PASS' \
  http://192.168.1.100/ISAPI/System/deviceInfo

# Probar estabilidad real de la red (0% = bien)
ping -c 30 -s 1472 192.168.1.100

Sobre las URLs RTSP de Hikvision:

# Formato moderno
rtsp://IP:554/Streaming/Channels/101   # canal 1, stream principal
rtsp://IP:554/Streaming/Channels/102   # canal 1, sub-stream

# Formato antiguo (firmware viejo)
rtsp://IP:554/h264/ch1/main/av_stream

Conclusión: lecciones del diagnóstico RTSP

Lo que más me sirvió de este diagnóstico:

Si vas a comprar esta cámara o una parecida, revisa qué trae la caja: si no incluye adaptador, consigue uno de 12V DC, 1A o más, centro-positivo, o resuélvelo con PoE y un inyector. Es más barato que perder una tarde persiguiendo un fantasma en la red.

Un par de notas de seguridad que dejé pendientes: la contraseña quedó en el historial de zsh al aparecer dentro de las URLs de ffprobe y ffplay, así que conviene limpiar el historial y cambiar la contraseña desde la web.

Con el stream RTSP en H.264 ya estable, el siguiente paso natural es integrarlo con Home Assistant o Frigate para grabación y detección de movimiento.

← Volver al blog