- Helix ist eine AI-Plattform, die Nutzern den Bildschirm autonomer Coding-Agenten in der Cloud zeigt; stabile Remote-Bildübertragung ist dabei entscheidend
- Als WebRTC-basiertes Streaming an UDP-Sperren und Firewall-Beschränkungen in Unternehmensnetzwerken scheiterte, baute das Team eine WebSocket-basierte H.264-Pipeline auf, doch in instabilen WLAN-Umgebungen kam es zu massiver Latenz
- Statt einer komplexen Encoding-/Decoding-Struktur stellte sich heraus, dass das einfache periodische Übertragen von JPEG-Screenshots per HTTP deutlich stabiler und effizienter war
- Dieser Ansatz verbraucht weniger Bandbreite, benötigt keine Wiederherstellung beschädigter Frames und passt Bildqualität und Framerate automatisch an die Netzwerkqualität an
- Helix entschied sich schließlich für eine hybride Architektur: bei guten Verbindungen H.264, bei schlechten Verbindungen JPEG-Polling – ein simples, aber praxisnahes Remote-Streaming-System
Die Streaming-Probleme und Einschränkungen von Helix
- Helix ist eine Plattform, die den Bildschirm eines AI-Coding-Agenten, der in einer Cloud-Sandbox läuft, in Echtzeit teilen muss
- Nutzer verfolgen dabei wie bei einem Remote-Desktop, wie die AI Code schreibt
- Anfangs wurde WebRTC verwendet, doch Verbindungen scheiterten an der UDP-Blockierung in Unternehmensnetzwerken
- TURN-Server, STUN/ICE und benutzerdefinierte Ports wurden allesamt von Firewall-Richtlinien blockiert
- Deshalb wurde eine WebSocket-basierte H.264-Streaming-Pipeline, die ausschließlich HTTPS (Port 443) nutzt, direkt selbst implementiert
- Hardware-Encoding mit GStreamer + VA-API, Browser-Decoding mit WebCodecs
- Erreicht wurden 60fps, 40Mbps und weniger als 100ms Latenz
Netzwerklatenz und Leistungsabfall
- In instabilen Netzwerkumgebungen wie Cafés kam es dazu, dass das Bild stehen blieb oder um mehrere Dutzend Sekunden verzögert war
- Das TCP-basierte WebSocket führt bei Paketverlust dazu, dass sich Frames der Reihe nach verzögern und dadurch die Echtzeitfähigkeit zusammenbricht
- Auch eine niedrigere Bitrate löste das Latenzproblem nicht, sondern verschlechterte nur die Bildqualität
- Es wurde auch versucht, nur Keyframes zu senden, doch das scheiterte, weil das Moonlight-Protokoll P-Frames voraussetzt
Entdeckung des JPEG-Screenshot-Ansatzes
- Beim Debugging wurde der Endpunkt
/screenshot?format=jpeg&quality=70 aufgerufen, woraufhin sofort ein scharfes Bild geladen wurde
- Ein einzelnes JPEG mit 150KB wurde ohne Verzögerung angezeigt
- Durch einfaches wiederholtes Senden von HTTP-Anfragen zur Aktualisierung des Screenshots war eine flüssige Bildschirmaktualisierung auf dem Niveau von 5fps möglich
- Am Ende wurde die komplexe Video-Pipeline durch einen periodischen JPEG-Request-Ansatz (fetch loop) ersetzt
Vorteile des JPEG-Ansatzes
- Wichtige Vergleichspunkte gegenüber H.264
- Bandbreite: H.264 konstant 40Mbps, JPEG variabel zwischen 100~500Kbps
- Zustandsverwaltung: H.264 ist zustandsabhängig, JPEG sind vollständig unabhängige Frames
- Wiederherstellbarkeit: H.264 muss auf Keyframes warten, JPEG erholt sich sofort mit dem nächsten Frame
- Komplexität: H.264 erforderte monatelange Entwicklung, JPEG ließ sich mit ein paar Zeilen
fetch()-Loop implementieren
- Je schlechter die Netzwerkqualität, desto stabiler und effizienter erwies sich der einfache JPEG-Ansatz
Hybride Umschaltarchitektur
- Helix schaltet die beiden Verfahren automatisch anhand der RTT (Round-Trip Time) um
- RTT < 150ms → H.264-Streaming
- RTT > 150ms → JPEG-Polling
- Bei wiederhergestellter Verbindung kann der Nutzer per Klick zurückwechseln
- Eingabeereignisse (Tastatur/Maus) werden weiterhin per WebSocket übertragen, sodass die Interaktivität erhalten bleibt
- Der Server stoppt die Videoübertragung mit der Nachricht
{"set_video_enabled": false} und wechselt in den Screenshot-Modus
Problem mit instabilem Umschalten (Oscillation) und Lösung
- Wenn nach dem Stoppen der Übertragung der WebSocket-Traffic sinkt, fällt auch die Latenz – dadurch entstand eine Endlosschleife, in der automatisch wieder in den Videomodus gewechselt wurde
- Lösung: Nach dem Wechsel in den Screenshot-Modus bleibt dieser fix bestehen, bis der Nutzer klickt
- In der UI wird die Meldung „Video pausiert, um Bandbreite zu sparen“ angezeigt
Probleme mit JPEG-Unterstützung und Build-Prozess
- Beim Wayland-Screenshot-Tool grim ist in den Standardpaketen von Ubuntu die JPEG-Unterstützung deaktiviert
- Beim Ausführen von
grim -t jpeg erscheint der Fehler „jpeg support disabled“
- Zur Behebung wurde grim im Dockerfile direkt aus dem Quellcode gebaut, inklusive libjpeg-turbo8-dev
Endgültige Architektur
- Gute Verbindung: 60fps H.264, hardwarebeschleunigt
- Schlechte Verbindung: 2~10fps JPEG-Polling, vollständig zuverlässig
- Die Screenshot-Qualität wird abhängig von der Übertragungszeit automatisch angepasst
- Über 500ms: Qualität -10%, unter 300ms: +5%, mindestens 2fps
Zentrale Erkenntnisse
- Eine einfache Lösung ist besser als ein komplexes System — 2 Stunden JPEG-Hacking waren praxisnäher als 3 Monate H.264-Entwicklung
- Graceful Degradation ist entscheidend für die User Experience
- WebSocket eignet sich optimal für die Übertragung von Eingaben, ist für Videoübertragung aber nicht zwingend nötig
- Ubuntu-Pakete können Funktionen weglassen — bei Bedarf selbst bauen
- Vor der Optimierung erst messen — komplexes Streaming ist nicht zwangsläufig die einzige Lösung
Open-Source-Veröffentlichung
- Helix wird als Open Source bereitgestellt; die Kernimplementierung umfasst unter anderem
api/cmd/screenshot-server/main.go — Screenshot-Server
MoonlightStreamViewer.tsx — adaptive Client-Logik
websocket-stream.ts — Steuerung der Video-Umschaltung
- Helix wird mit dem Ziel entwickelt, AI-Infrastruktur zu schaffen, die auch in realen Umgebungen funktioniert
Noch keine Kommentare.