- Helix es una plataforma de IA que muestra a los usuarios la pantalla donde operan agentes autónomos de programación en la nube, por lo que la transmisión remota de pantalla estable es fundamental
- Cuando el streaming basado en WebRTC falló por el bloqueo de UDP y las restricciones de firewall en redes empresariales, el equipo construyó un pipeline H.264 sobre WebSocket, pero en Wi-Fi inestable aparecieron latencias severas
- En lugar de una estructura compleja de codificación y decodificación, descubrieron que simplemente enviar capturas JPEG periódicamente por HTTP era mucho más estable y eficiente
- Este enfoque usa menos ancho de banda, no requiere recuperación de cuadros dañados y ajusta automáticamente la calidad y la tasa de cuadros según la calidad de la red
- Como resultado, Helix adoptó una arquitectura híbrida: H.264 con buena conexión y sondeo de JPEG con mala conexión, completando un sistema de streaming remoto simple pero práctico
El problema de streaming y las restricciones de Helix
- Helix es una plataforma que debe compartir en tiempo real la pantalla de agentes de IA para programación que corren en sandboxes en la nube
- Los usuarios observan cómo la IA escribe código, como si fuera un escritorio remoto
- Al principio usaron WebRTC, pero la conexión fallaba por el bloqueo de UDP en redes empresariales
- Los servidores TURN, STUN/ICE y los puertos personalizados eran bloqueados por las políticas de firewall
- Por eso implementaron directamente un pipeline de streaming H.264 sobre WebSocket usando solo HTTPS (puerto 443)
- Codificación por hardware con GStreamer + VA-API y decodificación en el navegador con WebCodecs
- Alcanzaron 60fps, 40Mbps y menos de 100ms de latencia
Latencia de red y degradación del rendimiento
- En entornos de red inestables, como cafeterías, surgieron problemas donde el video se congelaba o se retrasaba decenas de segundos
- WebSocket basado en TCP provoca que, cuando hay pérdida de paquetes, los cuadros se retrasen en secuencia y se rompa el tiempo real
- Bajar el bitrate no resolvía la latencia; solo empeoraba la calidad de imagen
- También probaron enviar solo keyframes, pero falló porque el protocolo Moonlight requiere P-frames
El descubrimiento del método con capturas JPEG
- Durante la depuración, al llamar al endpoint
/screenshot?format=jpeg&quality=70, se cargó de inmediato una imagen nítida
- Un solo JPEG de 150KB se mostraba sin latencia
- Al simplemente repetir solicitudes HTTP para actualizar la captura, lograron una actualización de pantalla fluida de alrededor de 5fps
- Finalmente abandonaron el pipeline de video complejo y cambiaron al método de solicitudes JPEG periódicas (bucle de
fetch)
Ventajas del método JPEG
- Principales puntos de comparación frente a H.264
- Ancho de banda: H.264 fijo en 40Mbps, JPEG variable entre 100~500Kbps
- Gestión de estado: H.264 depende del estado, JPEG usa cuadros completamente independientes
- Recuperación: H.264 requiere esperar un keyframe, JPEG se recupera de inmediato con el siguiente cuadro
- Complejidad: H.264 tomó meses de desarrollo; JPEG se implementó con unas pocas líneas de bucle
fetch()
- Cuanto peor es la calidad de la red, el método simple con JPEG resulta más estable y eficiente
Estructura híbrida de conmutación
- Helix cambia automáticamente entre ambos métodos según el RTT (tiempo de ida y vuelta)
- RTT < 150ms → streaming H.264
- RTT > 150ms → sondeo JPEG
- Cuando la conexión se recupera, el usuario hace clic para volver a cambiar
- Los eventos de entrada (teclado y mouse) siguen enviándose por WebSocket, para mantener la interactividad
- El servidor detiene la transmisión de video y cambia al modo de capturas con el mensaje
{"set_video_enabled": false}
Problema de inestabilidad en la conmutación (oscillation) y solución
- Cuando se detiene la transmisión, el tráfico de WebSocket baja, la latencia mejora y se produce un bucle infinito en el que el sistema vuelve automáticamente al modo de video
- Solución: una vez que entra en modo de capturas, permanece fijo hasta que el usuario haga clic
- La UI muestra el mensaje “video pausado para ahorrar ancho de banda”
Problema de soporte JPEG y proceso de compilación
- La herramienta de capturas para Wayland grim tiene el soporte JPEG desactivado en el paquete predeterminado de Ubuntu
- Al ejecutar
grim -t jpeg aparece el error “jpeg support disabled”
- Para resolverlo, incluyeron libjpeg-turbo8-dev en el Dockerfile y compilaron grim directamente desde el código fuente
Arquitectura final
- Buena conexión: H.264 a 60fps, con aceleración por hardware
- Mala conexión: sondeo JPEG de 2~10fps, con confiabilidad total
- La calidad de las capturas se ajusta automáticamente según el tiempo de transmisión
- Si supera 500ms, calidad -10%; si baja de 300ms, +5%; manteniendo un mínimo de 2fps
Principales aprendizajes
- Una solución simple supera a un sistema complejo — 2 horas de hack con JPEG fueron más prácticas que 3 meses de desarrollo con H.264
- La degradación elegante (graceful degradation) es clave para la experiencia del usuario
- WebSocket es ideal para transmitir entrada, pero no es indispensable para video
- Los paquetes de Ubuntu pueden venir con funciones faltantes — si hace falta, hay que compilar directamente
- Medir antes de optimizar es esencial — el streaming complejo no es la única solución
Publicación como open source
- Helix se ofrece como open source, y la implementación principal incluye
api/cmd/screenshot-server/main.go — servidor de capturas
MoonlightStreamViewer.tsx — lógica adaptativa del cliente
websocket-stream.ts — control de conmutación de video
- Helix se está desarrollando con el objetivo de ser una infraestructura de IA que funcione también en entornos reales
1 comentarios
Comentarios de Hacker News
Cuando la red es mala, que JPEG se degrade no se debe a UDP sino a la forma en que se implementa TCP
JPEG no resuelve los problemas de buffering ni de control de congestión. Lo más probable es que se haya implementado con una estructura que minimiza la transmisión de frames
h.264 tiene una eficiencia de codificación mayor que JPEG. Con el mismo tamaño, un frame IDR de h.264 puede ofrecer mejor calidad
El problema de fondo es la ausencia de estimación de ancho de banda. Incluso en un entorno TCP se puede ajustar el bitrate mediante sondeo inicial de ancho de banda y detección de retraso de transmisión
Si es posible, es mejor usar WebRTC, y para evadir firewalls conviene usar WebSocket
Incluso dejando de lado los problemas de formato del texto o su estilo de LLM, hay muchas cosas equivocadas en el contenido
10Mbps debería ser suficiente para una pantalla estática. El problema es que la configuración de codificación está mal o que la calidad del encoder es baja
El enfoque de “mandar solo keyframes” es ineficiente; en su lugar, bastaría con configurar un intervalo corto de keyframes
Al final, el problema es la estructura de empujar todo el stream por una sola conexión TCP. Ya existen soluciones como DASH pensadas para este caso
Sería buena idea tomar como referencia lo que VNC viene haciendo desde 1998
Mantiene el modelo de cliente que hace polling, pero divide el framebuffer en mosaicos y solo transmite las partes modificadas
En una pantalla de código estática puede reducir mucho el ancho de banda. Si además detecta scroll, sería aún más eficiente
Antes trabajé con codificación de video, y 40Mbps es calidad nivel Blu-ray
Es excesivo para hacer streaming de texto simple. Tras hablarlo con Claude, la conclusión fue que 30FPS, GOP de 2 segundos y un promedio de 1Mbps serían suficientes
Incluso en el peor caso, con 1.2Mbps se puede mantener una calidad bastante estable
El problema central de este texto es que configura demasiado alto el ancho de banda mínimo de h.264
H.264 es mucho más eficiente que JPEG. Debieron empezar en 1Mbps e ir ajustando
Usar solo keyframes es, de hecho, ineficiente
Yo lo habría abordado de una forma totalmente distinta
10Mbps es excesivo, y los videos de programación en YouTube andan alrededor de 0.6Mbps incluso en 1080p. Se ven suficientemente nítidos
Preferiría bajar a 1fps o ajustar el intervalo de keyframes
Hacer streaming de video en tiempo real al navegador es realmente doloroso
Si las capturas JPEG funcionan bien, mejor dejarlas así
Stacks como gstreamer o Moonlight son un infierno de depurar si no entiendes backpressure y propagación de errores
Una alternativa realista es la combinación de NVIDIA Video Codec SDK + WebSocket + MediaSource Extensions
Pero si el texto fue generado por un LLM, da la impresión de que el autor no tendría intención de entender esa estructura interna
Hace tiempo usé un programa que tomaba capturas de pantalla cada 5 segundos, y el disco duro se llenó enseguida
Me di cuenta de que la mayoría de las imágenes eran iguales, y mientras pensaba en un algoritmo que guardara solo las partes cambiadas,
terminé dándome cuenta de que estaba reinventando la compresión de video
Lo resolví con una sola línea de ffmpeg y ahorré 98% de espacio de almacenamiento
Hacer streaming a 40Mbps de un video de un LLM tecleando es un ancho de banda anormalmente excesivo
La única forma de conseguir buenas respuestas en HN es publicar algo equivocado
Creo que este es un ejemplo perfecto de ese equilibrio: un texto incorrecto, pero lo bastante interesante como para generar discusión