1 points par GN⁺ 2025-12-24 | 1 commentaires | Partager sur WhatsApp
  • Helix est une plateforme d’IA qui montre à l’utilisateur l’écran sur lequel tournent des agents de codage autonomes dans le cloud, et la transmission distante fiable de l’écran y est essentielle
  • Quand le streaming basé sur WebRTC a échoué à cause du blocage de l’UDP et des contraintes de pare-feu sur les réseaux d’entreprise, l’équipe a construit un pipeline H.264 basé sur WebSocket, mais dans des environnements Wi-Fi instables, la latence est devenue très importante
  • Au lieu d’une architecture complexe d’encodage et de décodage, l’équipe a découvert qu’une approche consistant simplement à envoyer périodiquement des captures d’écran JPEG via HTTP était bien plus stable et efficace
  • Cette méthode consomme moins de bande passante, n’a pas besoin de récupération de trames corrompues et ajuste automatiquement la qualité d’image et la fréquence d’images selon la qualité du réseau
  • Au final, Helix a adopté une architecture hybride : H.264 sur une bonne connexion et bascule vers le polling JPEG sur une mauvaise connexion, aboutissant à un système de streaming distant simple mais pragmatique

Le problème de streaming et les contraintes de Helix

  • Helix est une plateforme qui doit partager en temps réel l’écran d’un agent de codage IA exécuté dans un sandbox cloud
    • L’utilisateur regarde l’IA écrire du code comme s’il s’agissait d’un bureau à distance
  • Au départ, l’équipe utilisait WebRTC, mais la connexion échouait à cause du blocage de l’UDP sur les réseaux d’entreprise
    • Serveurs TURN, STUN/ICE, ports personnalisés : tout était bloqué par les politiques de pare-feu
  • L’équipe a donc implémenté elle-même un pipeline de streaming H.264 basé sur WebSocket n’utilisant que HTTPS (port 443)
    • Encodage matériel avec GStreamer + VA-API, décodage navigateur avec WebCodecs
    • 60fps, 40Mbps et moins de 100ms de latence atteints

Latence réseau et dégradation des performances

  • Dans des environnements réseau instables comme les cafés, la vidéo se figeait ou prenait plusieurs dizaines de secondes de retard
    • Avec WebSocket basé sur TCP, en cas de perte de paquets, les trames étaient retardées séquentiellement et le temps réel s’effondrait
  • Réduire le bitrate ne résolvait pas la latence et ne faisait que dégrader la qualité d’image
  • L’équipe a aussi tenté d’envoyer uniquement des keyframes, mais cela a échoué parce que le protocole Moonlight exige des P-frames

La découverte de l’approche par captures JPEG

  • Pendant le débogage, l’appel à l’endpoint /screenshot?format=jpeg&quality=70 a chargé instantanément une image nette
    • Un JPEG unique de 150KB s’affichait sans latence
  • Le simple fait de répéter des requêtes HTTP pour rafraîchir la capture permettait une mise à jour fluide de l’écran autour de 5fps
  • L’équipe a donc abandonné le pipeline vidéo complexe au profit d’une méthode de requêtes JPEG périodiques (boucle fetch())

Les avantages de l’approche JPEG

  • Principaux points de comparaison face à H.264
    • Bande passante : H.264 reste fixe à 40Mbps, JPEG varie entre 100 et 500Kbps
    • Gestion d’état : H.264 dépend de l’état, JPEG fournit des trames totalement indépendantes
    • Récupération : H.264 impose d’attendre une keyframe, JPEG récupère immédiatement à la trame suivante
    • Complexité : H.264 a demandé plusieurs mois de développement, JPEG s’implémente avec quelques lignes de boucle fetch()
  • Plus la qualité du réseau est mauvaise, plus l’approche JPEG, pourtant simple, se montre stable et efficace

Architecture hybride de bascule

  • Helix bascule automatiquement entre les deux méthodes selon le RTT (temps d’aller-retour)
    1. RTT < 150ms → streaming H.264
    2. RTT > 150ms → polling JPEG
    3. Une fois la connexion rétablie, l’utilisateur clique pour rebascule
  • Les événements d’entrée (clavier, souris) continuent d’être envoyés via WebSocket, ce qui préserve l’interactivité
  • Le serveur arrête l’envoi vidéo et passe en mode capture avec le message {"set_video_enabled": false}

Problème d’oscillation et solution

  • Une fois la transmission vidéo arrêtée, le trafic WebSocket diminuait, la latence baissait et cela déclenchait une boucle infinie de retour automatique en mode vidéo
  • Solution : après l’entrée en mode capture, rester verrouillé jusqu’au clic de l’utilisateur
    • L’interface affiche le message « Vidéo mise en pause pour économiser la bande passante »

Problème de support JPEG et processus de build

  • L’outil de capture Wayland grim est livré dans les paquets Ubuntu par défaut avec le support JPEG désactivé
    • La commande grim -t jpeg renvoie l’erreur « jpeg support disabled »
  • Pour corriger cela, l’équipe a compilé directement grim depuis les sources dans le Dockerfile en incluant libjpeg-turbo8-dev

Architecture finale

  • Bonne connexion : H.264 à 60fps, accélération matérielle
  • Mauvaise connexion : polling JPEG à 2~10fps, fiabilité totale
  • La qualité des captures est ajustée automatiquement selon le temps de transfert
    • Au-delà de 500ms : qualité -10 %, en dessous de 300ms : +5 %, avec un minimum maintenu à 2fps

Enseignements clés

  1. Une solution simple vaut mieux qu’un système complexe — 2 heures de bricolage JPEG ont été plus utiles que 3 mois de développement H.264
  2. La dégradation élégante (graceful degradation) est au cœur de l’expérience utilisateur
  3. WebSocket est optimal pour transmettre les entrées, mais pas indispensable pour la vidéo
  4. Les paquets Ubuntu peuvent manquer de fonctionnalités — il faut parfois compiler soi-même
  5. Mesurer avant d’optimiser est indispensable — un streaming complexe n’est pas forcément la seule solution

Publication en open source

  • Helix est proposé en open source, avec notamment ces éléments clés
    • api/cmd/screenshot-server/main.go — serveur de captures d’écran
    • MoonlightStreamViewer.tsx — logique client adaptative
    • websocket-stream.ts — contrôle de la bascule vidéo
  • Helix est développé avec l’objectif de fournir une infrastructure IA capable de fonctionner aussi en conditions réelles

1 commentaires

 
GN⁺ 2025-12-24
Avis sur Hacker News
  • Quand le réseau est mauvais, si le JPEG s’en sort mieux, ce n’est pas à cause de l’UDP mais de la façon dont TCP est implémenté
    Le JPEG ne résout ni les problèmes de mise en tampon ni ceux de contrôle de congestion. Il est plus probable que l’implémentation réduise au minimum l’envoi des frames
    h.264 a une meilleure efficacité de codage que le JPEG. À taille égale, une frame IDR h.264 peut offrir une meilleure qualité
    Le vrai problème est l’absence d’estimation de la bande passante. Même dans un environnement TCP, on peut ajuster le bitrate via une sonde initiale de bande passante et la détection de la latence de transmission
    Si possible, mieux vaut utiliser WebRTC, et WebSocket pour contourner les pare-feu

    • Dans le code de polling cité dans l’article, la requête suivante n’était envoyée qu’une fois le téléchargement du JPEG précédent terminé. Ce type de boucle est possible même sans UDP
    • Il est probable que les frames aient été entièrement sérialisées pour n’être demandées qu’une par une, ou qu’une nouvelle connexion soit ouverte à chaque fois via une nouvelle requête GET
    • Point intéressant : même des JPEG visuellement identiques peuvent voir leur taille réduite à 10–15 %. À la fin des années 2000, travailler sur l’optimisation des performances web avec ce genre de gains d’efficacité était très gratifiant
  • Même en laissant de côté les problèmes de forme ou le style LLM du billet, le fond contient beaucoup d’erreurs
    10 Mbps devraient suffire pour un écran statique. Le problème vient soit de paramètres d’encodage mal réglés, soit d’un encodeur de mauvaise qualité
    L’approche consistant à « n’envoyer que des keyframes » est inefficace ; il suffit plutôt de définir un intervalle de keyframes court
    Au fond, le problème est une architecture qui pousse tout le flux dans une unique connexion TCP. Des solutions comme DASH existent déjà pour ce type de cas

    • Je ne comprends pas pourquoi des textes écrits par IA remontent en tête. Lire des billets rédigés sans soin me paraît être une perte de temps
    • DASH n’est pas pris en charge chez Apple. HLS peut être une alternative, mais sans ffmpeg, c’est très difficile à mettre en œuvre
    • Ce billet donne au contraire très peu une impression de style LLM. Les critiques sans fondement ne sont pas convaincantes
    • Comme le temps et le coût nécessaires pour apprendre les outils existants sont élevés, une « réinvention erronée » peut malgré tout être plus efficace selon le contexte
  • Il serait utile de s’inspirer de ce que VNC fait depuis 1998
    L’idée est de garder un modèle de client en pull, en découpant le framebuffer en tuiles et en n’envoyant que les parties modifiées
    Sur un écran de code statique, cela peut réduire fortement la bande passante. Ajouter une détection du défilement le rendrait encore plus efficace

    • Parmi les différentes propositions, c’est celle qui me semble être le point de départ le plus réaliste. Partir sur 40 Mbps par défaut montre surtout que l’approche du problème était mauvaise
    • Le texte m’a semblé assez amateur. Je me demande si cette approche est possible aussi en open source
    • Je recommanderais de regarder d’abord le projet neko. Il gère bien mieux que VNC les problèmes de latence de connexion et de backpressure
    • Reproduire l’approche de VNC me paraît être la première tentative la plus naturelle. Utiliser une solution de jeu à faible latence comme Moonlight serait au contraire inadapté
  • J’ai déjà travaillé sur l’encodage vidéo, et 40 Mbps, c’est une qualité de niveau Blu-ray
    C’est excessif pour du simple streaming de texte. Après en avoir discuté avec Claude, la conclusion était qu’environ 30 FPS, un GOP de 2 secondes et un débit moyen d’environ 1 Mbps suffiraient
    Même dans le pire des cas, 1,2 Mbps suffiraient à maintenir une qualité stable

  • Le problème central de ce billet, c’est d’avoir réglé bien trop haut la bande passante minimale pour h.264
    H.264 est bien plus efficace que le JPEG. Il aurait fallu commencer à 1 Mbps puis ajuster
    N’utiliser que des keyframes est au contraire inefficace

    • Le billet dit que « en abaissant à 10 Mbps, on obtenait 30 secondes de latence », mais cela vient probablement des paramètres d’encodage
    • Le JPEG aussi peut atténuer les saccades via une mise en tampon créant une file de lecture. Les lecteurs modernes surveillent aujourd’hui la qualité réseau en temps réel
  • À ma place, j’aurais adopté une approche complètement différente
    10 Mbps, c’est excessif, et les vidéos de code sur YouTube tournent autour de 0,6 Mbps même en 1080p. C’est largement assez net
    Je pense qu’il vaudrait mieux descendre à 1 fps ou ajuster l’intervalle des keyframes

    • Le style et la progression logique du texte sentent le LLM. Le code doit être du même niveau
    • 1 fps pourrait être insuffisant. Il faudrait un réglage qui fasse de chaque frame une keyframe
    • Mais pour certaines personnes, même la qualité vidéo de YouTube peut être insupportablement gênante
  • Diffuser de la vidéo en temps réel dans un navigateur est vraiment pénible
    Si les captures d’écran JPEG fonctionnent bien, autant les garder
    Des stacks comme gstreamer ou Moonlight sont un enfer à déboguer si on ne comprend pas la backpressure et la propagation des erreurs
    Une combinaison NVIDIA Video Codec SDK + WebSocket + MediaSource Extensions est une alternative réaliste
    Mais si le billet a été généré par LLM, l’auteur n’a probablement pas envie de comprendre ce fonctionnement interne

    • Quand il faut traiter ce genre de système complexe pour un objectif unique, les LLM peuvent au contraire être utiles
  • J’ai autrefois utilisé un programme qui prenait une capture d’écran toutes les 5 secondes, et le disque dur s’est vite rempli
    En réalisant que la plupart des images étaient identiques, j’ai réfléchi à un algorithme ne stockant que les parties modifiées, puis
    je me suis aperçu que j’étais tout simplement en train de réinventer la compression vidéo
    Une ligne de ffmpeg a suffi à régler le problème, avec 98 % d’espace économisé

  • Diffuser à 40 Mbps une vidéo d’un LLM en train de taper représente une bande passante anormalement excessive

    • Et puis regarder à 60 fps « un ordinateur en train de taper » est en soi étrange. C’est une approche qui ne comprend absolument pas le domaine du problème
  • Sur HN, la seule façon d’obtenir de bonnes réponses est de publier quelque chose de faux
    À mon avis, c’est justement un excellent exemple d’équilibre : un texte faux, mais intéressant, parfait pour déclencher une discussion