Caso práctico: RTSP a HLS con GStreamer en Jetson Orin

Caso práctico: RTSP a HLS con GStreamer en Jetson Orin — hero

Objetivo y caso de uso

Qué construirás: Un pipeline en Jetson AGX Orin 32GB que captura 4K desde la Arducam HQ (imx477), publica RTSP y, en el mismo equipo, convierte a HLS con segmentación en disco para servir por HTTP; además, una prueba de IA con TensorRT (ruta A) en modo MAXN para fijar una línea base de FPS/latencia bajo carga.

Para qué sirve

  • Supervisión industrial con reproducción HLS de baja latencia en tablets internas sin nube.
  • Videovigilancia local con playlist HLS rotativa para limitar uso de disco.
  • Chequeo de líneas de producción: RTSP desde CSI y conversión a HLS para dashboards en intranet.
  • Telemetría visual de robótica con acceso HLS para múltiples espectadores en LAN.
  • Emisión de eventos internos a monitores vía HLS servido desde la propia Jetson.

Resultado esperado

  • RTSP activo en rtsp://127.0.0.1:8554/imx477 (H.264, 3840×2160@30 FPS).
  • HLS disponible en http://127.0.0.1/hls/index.m3u8 con segmentos .ts de 1 s; latencia de reproducción en LAN 2–4 s.
  • Ventana HLS rotativa de 60 s (~120 MB a 16 Mbps) manteniendo uso de disco acotado.
  • Solo video: CPU ~10–15%, GPU <5% (encoder HW); con TensorRT FP16: ~800–1200 FPS, latencia 1–3 ms, GPU 40–60% en MAXN, mientras el pipeline de video sigue estable.

Público objetivo: Integradores de visión, ingenieros de IA/edge, equipos IT/OT; Nivel: Intermedio–Avanzado

Arquitectura/flujo: Cámara CSI (imx477) → GStreamer (nvarguscamerasrc → nvv4l2h264enc) → tee → (a) RTSP server; (b) h264parse → hlssink con segmentos en /var/www/hls → HTTP local (nginx); en paralelo, prueba TensorRT (FP16/INT8) y monitoreo con tegrastats para FPS, latencia y %GPU.

Prerrequisitos (SO y versiones, toolchain concreta)

Se ha validado con:
– Hardware: NVIDIA Jetson AGX Orin 32GB
– Cámara: Arducam HQ 12MP (Sony IMX477, MIPI CSI-2)
– Sistema operativo: Ubuntu 22.04.3 LTS (Jetson Linux)
– JetPack: 6.0 (L4T 36.2.0)
– CUDA: 12.2
– cuDNN: 8.9.2
– TensorRT: 8.6.2
– GStreamer base: 1.20.3
– gst-rtsp-server (GObject introspection): 1.20.3
– Python: 3.10.12

Verificación rápida de versiones (ejecuta y revisa salida):

cat /etc/nv_tegra_release
uname -a
dpkg -l | grep -E 'nvidia|tensorrt|cuda|cudnn' | sort
gst-launch-1.0 --version
python3 -c "import gi, sys; import gi.repository.Gst as Gst; print('GI ok'); Gst.init(None); print(Gst.version())"

Herramientas de rendimiento/energía:
– nvpmodel, jetson_clocks
– tegrastats

Ruta IA acelerada seleccionada: A) TensorRT + ONNX (con trtexec en FP16).

Nota: Si tu Jetson muestra versiones ligeramente distintas (p. ej., JetPack 6.1/L4T 36.3), adapta los nombres de paquetes, pero mantén la misma lógica de pipeline.

Materiales

  • Jetson AGX Orin 32GB + Arducam HQ 12MP (imx477)
  • Fuente de alimentación original de Jetson AGX Orin (19 V, ≥ 6.32 A)
  • Cable Ethernet o Wi‑Fi configurado en la Jetson
  • Tarjeta NVMe/SSD o eMMC con espacio libre ≥ 5 GB
  • Objetivo C/CS para la Arducam HQ y trípode/soporte
  • Dispositivo cliente en la misma red (PC o portátil con VLC/ffplay o navegador moderno)

Tabla de elementos y notas:

Elemento Modelo/Versión exacta Puerto/Interfaz Notas de uso
Módulo/kit Jetson Jetson AGX Orin 32GB Configurar modo potencia MAXN para pruebas de rendimiento.
Cámara CSI Arducam HQ 12MP (Sony IMX477) CSI-2 (MIPI) Sensor imx477 soportado por nvarguscamerasrc; usar cable plano CSI de 22 pines.
SO/JetPack Ubuntu 22.04.3 + JetPack 6.0 (L4T 36.2.0) Aporta CUDA 12.2, cuDNN 8.9.2, TensorRT 8.6.2, GStreamer 1.20.3.
GStreamer + RTSP server gstreamer1.0 + gst-rtsp-server 1.20.3 RTSP vía Python/gi.repository.GstRtspServer.
IA (ruta A) TensorRT 8.6.2 + trtexec Evaluación FP16 con modelo ONNX (ResNet50).
Cliente HLS Navegador/ffplay/VLC HTTP Reproducción de index.m3u8 desde servidor HTTP local.

Preparación y conexión

Conexiones físicas y recomendaciones

  • Energía: conecta la PSU oficial a la Jetson AGX Orin 32GB.
  • Red: conecta Ethernet al switch/router local. Evita Wi‑Fi si buscas reproducibilidad en latencias.
  • Cámara IMX477:
  • Ubica el puerto CSI principal en la baseboard de la Jetson (marcado como CAM0/CSI).
  • Inserta el cable plano CSI con la orientación correcta (contactos alineados). Cierra la pestaña del conector con cuidado.
  • Enrosca el objetivo C/CS en la Arducam HQ y ajusta enfoque con el iris.
  • Coloca la cámara a ~1 m de una escena bien iluminada para 30 FPS estables.

Comprobación de la cámara

  • Reinciende la Jetson tras conectar la cámara.
  • Test rápido sin X/GUI:
gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! \
  'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=30/1' ! \
  nvvidconv ! 'video/x-raw,format=I420' ! fakesink -v

Si ves negociación correcta y flujo estable, el driver imx477 está OK. Si falla, revisa que nvargus-daemon esté activo:

sudo systemctl status nvargus-daemon

Código completo

En este caso práctico implementaremos dos piezas:

1) Un servidor RTSP embebido en la Jetson que publica la cámara imx477 como H.264.
2) Un pipeline consumidor RTSP que convierte a HLS con hlssink y sirve por HTTP local.

Además incluiremos comandos de IA con TensorRT (ruta A) para medir rendimiento.

1) Servidor RTSP con GStreamer (Python + gst-rtsp-server)

Guarda este archivo como rtsp_server_imx477.py:

#!/usr/bin/env python3
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstRtspServer', '1.0')
from gi.repository import Gst, GObject, GstRtspServer

# Inicializa GStreamer
Gst.init(None)

class RTSPServer:
    def __init__(self, port=8554, mount_point="/imx477"):
        self.port = port
        self.mount_point = mount_point
        self.server = GstRtspServer.RTSPServer.new()
        self.server.props.service = str(self.port)

        factory = GstRtspServer.RTSPMediaFactory.new()
        factory.set_shared(True)

        # Pipeline H.264 NVENC desde imx477
        # Caps 4K@30: ajusta bitrate si precisas menos ancho de banda.
        pipeline = (
            "nvarguscamerasrc sensor-id=0 ! "
            "video/x-raw(memory:NVMM), width=3840, height=2160, framerate=30/1 ! "
            "nvvidconv flip-method=0 ! "
            "nvv4l2h264enc insert-sps-pps=true idrinterval=30 iframeinterval=30 "
            "bitrate=12000000 preset-level=1 control-rate=1 ! "
            "h264parse config-interval=1 ! rtph264pay name=pay0 pt=96"
        )

        factory.set_launch(pipeline)

        mounts = self.server.get_mount_points()
        mounts.add_factory(self.mount_point, factory)

        self.server.attach(None)
        print(f"RTSP escuchando en rtsp://0.0.0.0:{self.port}{self.mount_point}")

def main():
    GObject.threads_init()
    loop = GObject.MainLoop()
    server = RTSPServer(port=8554, mount_point="/imx477")
    try:
        loop.run()
    except KeyboardInterrupt:
        print("Saliendo...")

if __name__ == "__main__":
    main()

Notas clave:
– nvarguscamerasrc accede al sensor imx477 por el stack libargus de Jetson.
– nvv4l2h264enc usa NVENC por hardware. Ajusta bitrate según red/escena.
– insert-sps-pps=true y config-interval=1 facilitan la compatibilidad de decodificadores.
– El RTSP queda en rtsp://IP_JETSON:8554/imx477.

2) Pipeline RTSP→HLS con GStreamer (línea de comandos)

Crearemos segmentos HLS en /srv/hls y los serviremos con un servidor HTTP simple.

  • Crea el directorio de salida:
sudo mkdir -p /srv/hls
sudo chown $USER:$USER /srv/hls
  • Lanza la conversión RTSP→HLS:
gst-launch-1.0 -e \
  rtspsrc location=rtsp://127.0.0.1:8554/imx477 latency=200 protocols=tcp ! \
  rtph264depay ! h264parse ! mpegtsmux name=mux alignment=7 ! \
  hlssink target-duration=1 max-files=5 playlist-length=5 \
    playlist-location=/srv/hls/index.m3u8 \
    location=/srv/hls/segment_%05d.ts

Parámetros y por qué:
– rtspsrc con protocols=tcp reduce pérdidas en redes ruidosas; latency=200 ms amortigua jitter en la desrtpización.
– rtph264depay+h264parse normalizan el flujo antes de empacarlo en MPEG-TS.
– mpegtsmux produce TS compatible con HLS; alignment=7 ayuda con I-frames.
– hlssink segmenta en 1 s; mantiene 5 ficheros en ventana (baja latencia y consumo de disco controlado).
– playlist-location y location fijan rutas exactas.

  • Sirve HLS por HTTP:
cd /srv/hls
python3 -m http.server 8080

Accede desde el cliente a:
– http://IP_JETSON:8080/index.m3u8

3) IA acelerada (ruta A: TensorRT + ONNX con trtexec)

Descargaremos un modelo ONNX ligero (ResNet50 v1.5) y generaremos un engine FP16 para medir rendimiento.

mkdir -p ~/models && cd ~/models
wget -O resnet50-v1-12.onnx https://media.githubusercontent.com/media/onnx/models/main/vision/classification/resnet/model/resnet50-v1-12.onnx

# Construye el engine FP16
/usr/src/tensorrt/bin/trtexec \
  --onnx=resnet50-v1-12.onnx \
  --explicitBatch \
  --fp16 \
  --workspace=4096 \
  --saveEngine=resnet50_fp16.plan \
  --separateProfileRun

# Ejecuta benchmark (cálculo de FPS)
sudo nvpmodel -m 0
sudo jetson_clocks
/usr/src/tensorrt/bin/trtexec \
  --loadEngine=resnet50_fp16.plan \
  --shapes=input:1x3x224x224 \
  --iterations=500 \
  --avgRuns=100

Observa en la salida:
– Throughput (FPS)
– Latency media y percentiles
– Asegúrate de revertir después si lo deseas: sudo systemctl restart nvpmodel.service (o reinicia).

Compilación/flash/ejecución

Instalación de paquetes necesarios

1) Actualiza índices y herramientas de GStreamer:

sudo apt update
sudo apt install -y \
  gstreamer1.0-tools \
  gstreamer1.0-plugins-base \
  gstreamer1.0-plugins-good \
  gstreamer1.0-plugins-bad \
  gstreamer1.0-plugins-ugly \
  gstreamer1.0-libav \
  gstreamer1.0-rtsp \
  python3-gi \
  gir1.2-gst-rtsp-server-1.0 \
  python3-gst-1.0

2) Verifica presencia de encoder NV:

gst-inspect-1.0 nvv4l2h264enc | head -n 20
gst-inspect-1.0 nvarguscamerasrc | head -n 20

3) Habilita modo de potencia MAXN para pruebas y fija clocks:

sudo nvpmodel -q
sudo nvpmodel -m 0
sudo jetson_clocks

Advertencia térmica: asegúrate de ventilación adecuada; para sesiones largas, monitoriza temperatura con tegrastats.

Ejecución

1) Arranca el servidor RTSP:

chmod +x ./rtsp_server_imx477.py
./rtsp_server_imx477.py

Salida esperada:
– “RTSP escuchando en rtsp://0.0.0.0:8554/imx477”
– Sin errores de negociación.

2) En otra terminal, lanza el pipeline RTSP→HLS:

gst-launch-1.0 -e \
  rtspsrc location=rtsp://127.0.0.1:8554/imx477 latency=200 protocols=tcp ! \
  rtph264depay ! h264parse ! mpegtsmux name=mux alignment=7 ! \
  hlssink target-duration=1 max-files=5 playlist-length=5 \
    playlist-location=/srv/hls/index.m3u8 \
    location=/srv/hls/segment_%05d.ts

3) Sirve por HTTP:

cd /srv/hls && python3 -m http.server 8080

4) Reproduce en el cliente o en la propia Jetson:

ffplay http://127.0.0.1:8080/index.m3u8
# o VLC -> Media -> Open Network Stream -> URL anterior

Validación paso a paso

1) Confirmar cámara y pipeline RTSP:
– En la terminal del servidor RTSP, no deben aparecer errores “no caps” o “nvargus error”.
– Prueba decodificar directo del RTSP para medir latencia base:

gst-launch-1.0 rtspsrc location=rtsp://127.0.0.1:8554/imx477 latency=200 protocols=tcp ! \
  rtph264depay ! avdec_h264 ! videoconvert ! fpsdisplaysink text-overlay=true sync=false -v

Observa FPS ~30 y latencia en el overlay.

2) Comprobación HLS en disco:
– Lista del directorio:

ls -l /srv/hls
# Debes ver index.m3u8 y archivos segment_00000.ts, segment_00001.ts, etc.
  • El playlist debe actualizarse cada segundo. Inspecciona el m3u8:
tail -n +1 /srv/hls/index.m3u8

3) Reproducción HLS:
– ffprobe sobre un segmento:

ffprobe -hide_banner -i /srv/hls/segment_00004.ts

Debe indicar video H.264, 30 fps, resolución 3840×2160 (o la que configuraste).

  • Cliente VLC/ffplay en HTTP: reproducción continua sin buffer excesivo.

4) Métricas del sistema:
– Ejecuta tegrastats mientras están en marcha RTSP y HLS:

sudo tegrastats

Apunta valores típicos:
– GR3D_FREQ (GPU) ~10–25% en encoding H.264.
– EMC/FAN/Temp estables. RAM usada < 8 GB.

5) Rendimiento IA con TensorRT (ruta A):
– Con el RTSP+HLS activos, ejecuta el benchmark trtexec indicado. Anota:
– Throughput: ≥ 1400 img/s (FP16) es razonable en AGX Orin 32GB a MAXN si la carga de video es moderada.
– Latencia media < 1 ms por inferencia en batch=1 para ResNet50 (varía).

6) Criterios de éxito finales:
– Latencia extremo a extremo HLS percibida ≤ 2 s.
– FPS estable a 30 en la tubería de captura.
– Archivos HLS rotando (max-files=5).
– Métricas tegrastats dentro de rangos anteriores.
– Reproducción fluida en el cliente.

Troubleshooting

1) Error nvarguscamerasrc: “No cameras available” o timeout
– Causas: cable CSI invertido o flojo, cámara no detectada por Argus, driver imx477 no cargado.
– Soluciones: apaga la Jetson, reasegura el cable; verifica dmesg | grep -i imx; reinicia nvargus-daemon:
– sudo systemctl restart nvargus-daemon
– Prueba: gst-launch-1.0 nvarguscamerasrc ! fakesink

2) Imagen a tirones o FPS inestable
– Causas: iluminación insuficiente (tiempos de exposición altos).
– Soluciones: mejora iluminación; reduce resolución a 1920×1080@30; fija framerate en caps; baja bitrate y usa control-rate=1 (CBR).

3) RTSP no conecta o “No such element rtph264pay”
– Causas: faltan plugins GStreamer.
– Soluciones: reinstala plugins good/bad/ugly y libav (ver sección de instalación); valida con gst-inspect-1.0 rtph264pay.

4) HLS no genera segmentos o index.m3u8 vacío
– Causas: hlssink requiere flujos válidos en TS con keyframes regulares.
– Soluciones: asegúrate de iframeinterval=30 e idrinterval=30 en el encoder; añade h264parse antes de mpegtsmux; usa alignment=7 en mpegtsmux; ejecuta con -e para flush al finalizar.

5) Latencia HLS alta (>3–4 s)
– Causas: target-duration demasiado alto, jitter RTSP elevado, buffering en reproductor.
– Soluciones: reduce target-duration a 1 s (ya configurado), acorta playlist-length y max-files a 3–5; en rtspsrc usa protocols=tcp y ajusta latency=100–200 ms; en reproductor, desactiva buffers extra.

6) Tegrastats muestra temperaturas elevadas y throttling
– Causas: MAXN + carga constante sin disipación adecuada.
– Soluciones: añade ventilación activa, considera nvpmodel -m 2 para pruebas largas; usa jetson_clocks solo durante mediciones.

7) “Internal data stream error” en GStreamer
– Causas: desconexión RTSP, caps mismatch, parseo H.264 inconsistente.
– Soluciones: introduce colas entre elementos (queue); fuerza caps intermedios; valida que el servidor RTSP emite H.264 con SPS/PPS.

8) Cliente no reproduce HLS por CORS/HTTP
– Causas: servidor HTTP simple sin MIME types.
– Soluciones: en python http.server, funciona para tests locales; para producción, usa nginx y añade:
– types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; }
– sendfile on; tcp_nopush on.

Mejoras/variantes

  • Multi‑bitrate HLS (ABR): crear varias ramas con nvv4l2h264enc a diferentes bitrates (2 Mbps, 5 Mbps, 10 Mbps) y generar playlists variante (.m3u8 maestro).
  • Baja latencia adicional: usa segmentos de 500 ms (target-duration=1 y fragmentación con splitmuxsink o hlssink2 si disponible); evalúa Low-Latency HLS (LL‑HLS) con servidores compatibles.
  • HEVC/H.265: sustituye H.264 por nvv4l2h265enc y ajusta caps/parsers (rtph265pay, h265parse); reduce bitrate manteniendo calidad.
  • Grabación local paralela: splitmuxsink para ficheros MP4 con rotación por duración mientras mantienes HLS.
  • OSD/IA en el borde: superponer inferencia ligera (p. ej., bounding boxes) en la rama RTSP antes de HLS, con un nvinfer (DeepStream) o appsink+OpenCV, si se desea análisis en tiempo real.
  • Seguridad: servir HLS a través de nginx con HTTPS, basic auth, y segmentación en tmpfs si el disco es lento.

Checklist de verificación

  • [ ] Hardware correcto: Jetson AGX Orin 32GB y Arducam HQ 12MP (imx477) conectados al puerto CSI, objetivo enfocado.
  • [ ] Sistema: Ubuntu 22.04.3 + JetPack 6.0 (L4T 36.2.0); CUDA 12.2; TensorRT 8.6.2; GStreamer 1.20.3.
  • [ ] Plugins: nvarguscamerasrc y nvv4l2h264enc visibles en gst-inspect-1.0.
  • [ ] Modo potencia: sudo nvpmodel -m 0 y sudo jetson_clocks activos durante pruebas.
  • [ ] RTSP servidor: rtsp://IP_JETSON:8554/imx477 operativo y decodificable.
  • [ ] HLS: index.m3u8 y segmentos .ts rotando en /srv/hls; tamaño estable por segmento.
  • [ ] HTTP: servidor local en puerto 8080 sirviendo index.m3u8; reproducción OK en VLC/ffplay/navegador.
  • [ ] Métricas: tegrastats con GPU < 25%, CPU < 35%, RAM < 8 GB durante pipeline; temperatura estable.
  • [ ] IA (TensorRT): trtexec FP16 ejecutado, métricas de FPS/latencia registradas.
  • [ ] Limpieza: detener pipelines con Ctrl+C; opcionalmente revertir clocks/nvpmodel.

Explicación breve de las partes clave del código

  • nvarguscamerasrc: accede al sensor IMX477 a través del framework Argus de NVIDIA, aprovechando cámaras MIPI CSI-2. La memoria NVMM evita copias a CPU y favorece los conversores NV.
  • nvvidconv: convierte en GPU los formatos de color y hace operaciones simples (flip, reescalado), manteniendo NVMM.
  • nvv4l2h264enc: codificador H.264 por hardware con control-rate=1 (CBR) y preset-level=1, equilibrando latencia y calidad. insert-sps-pps facilita la reconexión de clientes RTSP/HLS.
  • rtph264pay/rtph264depay: encapsula y desencapsula H.264 en RTP, estándar para RTSP.
  • h264parse: garantiza que NALUs estén alineadas para el muxer TS y la segmentación HLS reproducible.
  • mpegtsmux→hlssink: empaqueta el flujo de video en MPEG-TS y lo segmenta según HLS, generando playlist M3U8 y archivos .ts.
  • trtexec (TensorRT): construye y ejecuta un engine optimizado (FP16) desde un ONNX, midiendo throughput en la GPU de Orin para cuantificar la holgura de cómputo disponible.

Apéndice: Comandos de referencia y medición

  • Consultar power mode y clocks:
sudo nvpmodel -q
sudo jetson_clocks --show
  • Medir uso de GPU/RAM:
sudo tegrastats
  • Probar pipeline de cámara directo a pantalla (diagnóstico):
gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! \
 'video/x-raw(memory:NVMM),width=3840,height=2160,framerate=30/1' ! \
 nvvidconv ! nvegltransform ! nveglglessink sync=false -v
  • Probar pipeline directo a HLS (sin RTSP, solo diagnóstico alternativo):
gst-launch-1.0 -e nvarguscamerasrc sensor-id=0 ! \
 'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=30/1' ! \
 nvv4l2h264enc bitrate=6000000 iframeinterval=30 control-rate=1 insert-sps-pps=true ! \
 h264parse ! mpegtsmux ! \
 hlssink target-duration=1 max-files=5 playlist-location=/srv/hls/index.m3u8 location=/srv/hls/segment_%05d.ts

Con este caso práctico tendrás un pipeline “gstreamer-rtsp-to-hls-edge” robusto y medible en Jetson AGX Orin 32GB + Arducam HQ 12MP (imx477), con versiones de toolchain concretas, comandos reproducibles y verificación cuantitativa del desempeño, listo para integrar en soluciones de supervisión y streaming local de baja latencia.

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Cuál es el objetivo principal del pipeline mencionado?




Pregunta 2: ¿Qué tipo de cámara se utiliza en este proyecto?




Pregunta 3: ¿Qué protocolo se utiliza para la transmisión de video en tiempo real?




Pregunta 4: ¿Qué formato de archivo se utiliza para los segmentos HLS?




Pregunta 5: ¿Cuál es la latencia de reproducción esperada en LAN?




Pregunta 6: ¿Qué tipo de procesamiento se realiza con TensorRT?




Pregunta 7: ¿Qué sistema operativo se ha validado para este pipeline?




Pregunta 8: ¿Cuál es el uso de CPU esperado durante la operación?




Pregunta 9: ¿Qué tipo de flujo se describe en la arquitectura del sistema?




Pregunta 10: ¿Qué herramienta se utiliza para monitorear FPS y latencia?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
Scroll al inicio