H.264 स्ट्रीमिंग को JPEG screenshots से बदलने पर यह बेहतर चला
(blog.helix.ml)- Helix एक AI platform है जो क्लाउड पर चलने वाले autonomous coding agents की स्क्रीन यूज़र को दिखाता है, और इसके लिए स्थिर remote screen transmission बेहद महत्वपूर्ण है
- enterprise network में UDP blocking और firewall restrictions की वजह से WebRTC-आधारित स्ट्रीमिंग विफल होने पर, टीम ने WebSocket-आधारित H.264 pipeline बनाई, लेकिन अस्थिर Wi‑Fi माहौल में latency बहुत बढ़ गई
- जटिल encoding·decoding structure की जगह, सिर्फ JPEG screenshots को HTTP के जरिए समय-समय पर भेजने का तरीका कहीं अधिक स्थिर और प्रभावी निकला
- इस तरीके में bandwidth usage कम है, corrupted frames को recover करने की ज़रूरत नहीं पड़ती, और network quality के अनुसार image quality और frame rate अपने-आप adjust हो जाते हैं
- नतीजे में Helix ने अच्छे connection पर H.264 और खराब connection पर JPEG polling में switch करने वाली hybrid architecture अपनाई, जिससे एक सरल लेकिन व्यावहारिक remote streaming system तैयार हुआ
Helix की स्ट्रीमिंग समस्या और सीमाएँ
- Helix एक ऐसा platform है जिसे cloud sandbox में चल रहे AI coding agent की स्क्रीन real time में share करनी होती है
- यूज़र remote desktop की तरह AI को code लिखते हुए देखता है
- शुरुआत में WebRTC इस्तेमाल किया गया, लेकिन enterprise network में UDP blocking के कारण connection fail हो गया
- TURN server, STUN/ICE, custom ports — सब firewall policy से block हो गए
- इसके बाद सिर्फ HTTPS (port 443) इस्तेमाल करने वाली WebSocket-आधारित H.264 streaming pipeline सीधे implement की गई
- GStreamer + VA-API से hardware encoding, WebCodecs से browser decoding
- 60fps, 40Mbps और 100ms से कम latency हासिल की गई
नेटवर्क latency और performance degradation
- coffee shop जैसी अस्थिर network conditions में video रुक जाना या कई दर्जन सेकंड की latency जैसी समस्या सामने आई
- TCP-आधारित WebSocket में packet loss होने पर frames क्रम से delay होते गए और real time responsiveness टूट गई
- bitrate कम करने पर भी latency की समस्या हल नहीं हुई, सिर्फ image quality खराब हुई
- केवल keyframes भेजने का तरीका भी आज़माया गया, लेकिन Moonlight protocol को P-frames चाहिए थे, इसलिए यह विफल रहा
JPEG screenshot तरीके की खोज
- debugging के दौरान
/screenshot?format=jpeg&quality=70endpoint को call करने पर तुरंत साफ image load हो गई- 150KB का एक JPEG बिना latency के दिख गया
- सिर्फ HTTP requests दोहराकर screenshot refresh करने पर लगभग 5fps की smooth screen refresh संभव हुई
- आखिरकार जटिल video pipeline की जगह periodic JPEG request (
fetch()loop) तरीके पर switch किया गया
JPEG तरीके के फायदे
- H.264 के मुकाबले मुख्य तुलना बिंदु
- bandwidth: H.264 में 40Mbps स्थिर, JPEG में 100~500Kbps तक परिवर्तनशील
- state management: H.264 state-dependent, JPEG पूरी तरह independent frames
- recoverability: H.264 में keyframe का इंतज़ार ज़रूरी, JPEG में अगले frame से तुरंत recovery
- complexity: H.264 के लिए कई महीने development, JPEG सिर्फ कुछ lines के
fetch()loop से implement
- network quality जितनी खराब हो, उतना ही सरल JPEG तरीका ज़्यादा स्थिर और प्रभावी साबित हुआ
hybrid switching architecture
- Helix दोनों तरीकों के बीच RTT (round-trip latency) के आधार पर अपने-आप switch करता है
- RTT < 150ms → H.264 streaming
- RTT > 150ms → JPEG polling
- connection recover होने पर यूज़र click करके फिर switch कर सकता है
- input events (keyboard·mouse) WebSocket से लगातार भेजे जाते हैं, इसलिए interactivity बनी रहती है
- server
{"set_video_enabled": false}message से video transmission रोककर screenshot mode में switch कर देता है
switching instability (oscillation) समस्या और समाधान
- video transmission बंद होने के बाद WebSocket traffic घटते ही latency कम हो जाती थी, जिससे अपने-आप फिर video mode में लौटने वाला infinite loop बन जाता था
- समाधान: screenshot mode में जाने के बाद यूज़र के click तक उसी mode में fixed रहना
- UI में “bandwidth बचाने के लिए video paused है” message दिखाया गया
JPEG support समस्या और build process
- Wayland के screenshot tool grim में Ubuntu के default package पर JPEG support disabled था
grim -t jpegचलाने पर “jpeg support disabled” error आया
- इसे हल करने के लिए Dockerfile में libjpeg-turbo8-dev जोड़कर grim को source से सीधे build किया गया
final architecture
- अच्छा connection: 60fps H.264, hardware acceleration
- खराब connection: 2~10fps JPEG polling, पूरी reliability
- screenshot quality transmission time के आधार पर अपने-आप adjust होती है
- 500ms से अधिक होने पर quality -10%, 300ms से कम होने पर +5%, minimum 2fps बनाए रखा जाता है
मुख्य सीख
- सरल समाधान जटिल सिस्टम से बेहतर हो सकता है — H.264 पर 3 महीने की development से ज़्यादा 2 घंटे का JPEG hack व्यावहारिक निकला
- graceful degradation ही user experience की कुंजी है
- WebSocket input transmission के लिए बेहतरीन है, video transmission के लिए अनिवार्य नहीं
- Ubuntu packages में features missing हो सकते हैं — ज़रूरत हो तो खुद build करें
- optimization से पहले measurement ज़रूरी है — जटिल streaming ही एकमात्र समाधान नहीं
open source release
- Helix open source के रूप में उपलब्ध है, और core implementation में ये शामिल हैं
api/cmd/screenshot-server/main.go— screenshot serverMoonlightStreamViewer.tsx— adaptive client logicwebsocket-stream.ts— video switching control
- Helix का विकास ऐसी AI infrastructure के लिए हो रहा है जो वास्तविक environments में भी काम करे
1 टिप्पणियां
Hacker News की राय
नेटवर्क खराब होने पर JPEG का बेहतर चलना UDP की वजह से नहीं बल्कि TCP implementation के तरीके की वजह से है
JPEG buffering या congestion control की समस्या हल नहीं करता। ज़्यादा संभावना है कि इसे फ्रेम ट्रांसफर को न्यूनतम रखने वाली संरचना में implement किया गया हो
h.264 की encoding efficiency JPEG से बेहतर है। समान आकार पर h.264 IDR frame बेहतर quality दे सकता है
असली समस्या bandwidth estimation की कमी है। TCP environment में भी शुरुआती bandwidth probe और transmission delay detection के ज़रिए bitrate adjust किया जा सकता है
अगर संभव हो तो WebRTC इस्तेमाल करना बेहतर है, और firewall bypass के लिए WebSocket अच्छा विकल्प है
लेख के format या LLM style को अलग भी रख दें, तो भी पूरी सामग्री में कई बातें गलत हैं
10Mbps static screen के लिए पर्याप्त होना चाहिए। समस्या encoding settings के गलत होने या encoder quality के खराब होने की है
“सिर्फ keyframe भेजें” वाला तरीका inefficient है; इसकी बजाय छोटा keyframe interval सेट करना चाहिए
आखिरकार समस्या TCP single connection में पूरे stream को ठूंसने वाली संरचना है। ऐसी स्थिति के लिए DASH जैसे solution पहले से मौजूद हैं
VNC 1998 से जो तरीका अपनाता आ रहा है, उसे देखना उपयोगी होगा
client pull model को बनाए रखते हुए framebuffer को tile units में बांटना, और सिर्फ बदले हुए हिस्से भेजना इसका मूल ढांचा है
static coding screen में इससे bandwidth बहुत कम हो सकती है। अगर scroll detection भी जोड़ दिया जाए तो और efficient होगा
मैंने पहले video encoding के साथ काम किया है, और 40Mbps Blu-ray स्तर की quality है
साधारण text streaming के लिए यह बहुत ज़्यादा है। Claude से बातचीत के बाद निष्कर्ष यह निकला कि 30FPS, GOP 2 सेकंड, और औसतन 1Mbps काफ़ी है
सबसे खराब स्थिति में भी 1.2Mbps स्थिर quality बनाए रखने के लिए पर्याप्त होगा
इस लेख की मूल समस्या h.264 minimum bandwidth को बहुत ऊँचा सेट करना है
H.264, JPEG से कहीं अधिक efficient है। 1Mbps से शुरू करके adjust करना चाहिए था
सिर्फ keyframe इस्तेमाल करना उल्टा inefficient है
मैं होता तो इस समस्या को बिल्कुल अलग तरीके से देखता
10Mbps बहुत ज़्यादा है, और YouTube के coding videos 1080p पर भी 0.6Mbps के आसपास होते हैं। वे काफ़ी साफ़ दिखते हैं
बेहतर यह होता कि 1fps तक घटाते या keyframe interval adjust करते
browser में real-time video stream करना सचमुच बहुत दर्दनाक काम है
अगर JPEG screenshots अच्छी तरह काम कर रहे हैं, तो उन्हें वैसे ही रहने देना बेहतर है
gstreamer या Moonlight जैसे stack, अगर backpressure और error propagation समझ में न आएँ, तो debugging को नर्क बना देते हैं
NVIDIA Video Codec SDK + WebSocket + MediaSource Extensions का संयोजन एक व्यावहारिक विकल्प है
लेकिन अगर लेख LLM-generated है, तो शायद लेखक में ऐसी आंतरिक संरचना समझने की इच्छा नहीं होगी
मैंने पहले एक ऐसा program इस्तेमाल किया था जो हर 5 सेकंड में screenshot लेता था, और hard disk बहुत जल्दी भर गई
फिर समझ आया कि ज़्यादातर images एक जैसी हैं, तो सिर्फ बदले हुए हिस्से save करने वाले algorithm पर सोचना शुरू किया
और आखिर में एहसास हुआ कि मैं दरअसल video compression को दोबारा invent कर रहा था
ffmpeg की एक लाइन से समस्या हल हो गई, और storage space में 98% की बचत हुई
LLM के typing video को 40Mbps पर stream करना असामान्य रूप से अत्यधिक bandwidth है
HN पर अच्छा जवाब पाने का एकमात्र तरीका गलत पोस्ट डालना है
मेरे हिसाब से यह उसी आदर्श संतुलन का उदाहरण है: गलत, लेकिन इतना दिलचस्प कि चर्चा शुरू हो जाए