La TV no se apaga
Cómo copiamos la señal del DVR al navegador sin dejar en negro la pantalla de la tienda
En resumen: Saqué una copia del video del DVR al portal interno sin tocar la franja que va a la TV de la tienda. Con un tee, una cola que descarta cuadros y el encoder en otro proceso, las copias de memoria siempre activas bajaron de 249 MB/s a 62 MB/s.
Cada vez que toqué la captura de video del DVR, la TV vertical de la tienda terminó en negro delante de los clientes. Esa pantalla muestra en vivo una franja de la cámara de la calle. La imagen sale del HDMI del DVR (el grabador de las cuatro cámaras), entra por una tarjeta de captura Blackmagic DeckLink y la recorta un proceso de GStreamer en la máquina del kiosko. Yo quería además el mosaico completo, las cuatro cámaras juntas, en el portal interno.
La TV de la tienda manda sobre el portal interno
La franja la mira quien pasa por la vereda. El mosaico en el portal me ahorra caminar al almacén para ver la pantalla del DVR. Esa jerarquía quedó escrita en el pipeline, la cadena de pasos por donde viaja el video: la rama del portal descarta cuadros cuando se atrasa, la de la TV nunca.
Analogía: El vigilante que mira el monitor de cámaras no debería además narrarle por teléfono al jefe lo que ve, porque se distrae de su trabajo real. Le pones una segunda pantalla al lado con la misma señal, y otra persona prende la cámara del teléfono solo cuando el jefe llama.
Un tee justo después de la captura
Puse un tee (una bifurcación de la señal) inmediatamente después de decklinkvideosrc, el elemento que lee la tarjeta. La rama A recorta la franja y la pinta en la TV, igual que antes. La rama B publica el cuadro completo de 1920x1080 en un socket de memoria compartida, un buzón en la RAM que otro proceso puede leer. Esa rama trabaja detrás de una cola "leaky": cuando se llena, tira los cuadros viejos en vez de hacer esperar al resto, así que un espectador atascado en el navegador ya no frena la rama de la TV.
El encoder NVENC comprime ese video con la GPU y es la parte más cara del sistema, así que corre en un proceso aparte. go2rtc, el servidor que reparte el video al navegador, lo arranca solo cuando alguien abre el stream en el portal.
El encoder adentro dejó la franja en negro
Mi primera versión fue la obvia y me equivoqué: puse NVENC dentro del mismo proceso de captura. Crear el contexto CUDA (la sesión de trabajo con la GPU) bloquea el arranque del pipeline. Eso retrasó el saludo inicial de la tarjeta, que negocia formato y frecuencia con el HDMI antes de pasar un solo cuadro, y la franja quedó en negro con el error not-negotiated. Lo revertí el mismo día y desde entonces el encoder nunca comparte proceso con la captura.
Después aparecieron dos fallas silenciosas, del tipo que más trabajo me da. La primera: shmsink, el elemento que escribe en la memoria compartida, falla sin levantar error si encuentra un socket viejo del arranque anterior, así que el stream del portal estuvo muerto mientras la TV se veía perfecta. Ahora el script borra el socket antes de arrancar. La segunda: la rama B copiaba 60 cuadros por segundo que nadie miraba, porque el descarte estaba después de la copia. Moví el descarte al productor y la rama quedó en 15 cuadros por segundo (fps).
Lo que cambió en números
El portal muestra el mosaico a 1080p y 15 fps. La rama siempre activa bajó de 249 MB/s a 62 MB/s de copias de memoria, y el proceso de captura pasó de entre 46% y 52% de CPU a 32%. Abrir el stream desde el portal no empeora ese costo de reposo, porque el proceso caro corre aparte y solo mientras alguien mira.
El watchdog que revisa cada 20 segundos si la franja de la TV se congeló no vigila la rama del portal, así que si esa rama vuelve a morir en silencio me voy a enterar cuando abra el navegador. El espectador del portal también ve saltos cuando su conexión se atrasa, y eso lo pedí así.
Lo que hago distinto ahora
En el documento de la VM del kiosko quedaron tres reglas:
- si un proceso nuevo comparte máquina con algo que ve el cliente, defino qué cola descarta y cuánto CPU le toca antes de escribir el pipeline
- cada rama nueva necesita su propia señal de vida, porque tuvimos las cuatro pantallas en negro 5 horas y 20 minutos mientras el sistema se reportaba sano
- lo caro arranca cuando alguien lo pide: ya había matado una detección de objetos que se comía un núcleo entero compitiendo con la reproducción de video, y hoy el encoder recién arranca cuando el jefe llama
Más en Infraestructura
Por ahora este es el único post aquí. Mira la sección o todos los posts.