ADR-002: Web Streaming Technology¶
Status: Accepted
Context¶
Users must be able to see and interact with a running Android device from a web browser without installing any client software. The streaming solution must work through a standard HTTPS ingress.
Options Considered¶
| Technology | Latency | Audio | Infra complexity | Decision |
|---|---|---|---|---|
| noVNC (WebSocket) | 100–300ms | No | Zero (bundled) | Rejected |
| Cuttlefish WebRTC (built-in) | 10–50ms | Yes | TURN server | Selected |
| google/android-emulator-webrtc | 10–50ms | Yes | Extra sidecar + TURN | Rejected |
| Custom MJPEG | 200–500ms | No | ffmpeg sidecar | Rejected |
Decision¶
Use Cuttlefish's built-in WebRTC relay on port 8443. No separate streaming sidecar.
Cuttlefish natively serves a WebRTC-enabled web page at https://{pod-ip}:8443. The dashboard surfaces the per-device URL in an <iframe> — the browser connects directly via WebRTC peer-to-peer (with TURN relay for NAT traversal).
This decision flows from ADR-001: by choosing Cuttlefish as the emulation backend, WebRTC comes for free. Using noVNC or a separate WebRTC frontend would add infra complexity without benefit.
TURN Server¶
Cross-NAT access (browser outside cluster network → emulator pod) requires a TURN relay. coturn is deployed as a Helm component (turn.enabled: true).
- TURN credentials stored in a Kubernetes Secret (
coturn-secret.yaml) - Never appear in values.yaml,
kubectl describe, or process args - For cluster-internal CI runners on the same network, STUN alone may suffice
Per-device stream URL pattern: https://{pod-name}.{pool.spec.streaming.domain}
Consequences¶
- WebRTC streaming is available from day one — no phased rollout
- Dashboard
<iframe>loads the Cuttlefish WebRTC page; browser handles WebRTC natively - TURN bandwidth must be budgeted per concurrent session (~1–5 Mbps for 1080p)
- coturn must have a reachable external IP (LoadBalancer service or hostNetwork)