Skip to content

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)