Skip to content

Commit de43ebd

Browse files
authored
refactor: streamline Android display streaming around shared video (#76)
* feat: Enhance Android H.264 streaming support and refactor related components - Updated StreamQualityLimits struct to be cloneable and public. - Introduced stream_quality_limits_for_payload function to resolve quality limits from payload. - Refactored Android H.264 socket handling to utilize new quality resolution logic. - Renamed native encoder function to encode BGRA frames instead of RGBA. - Removed deprecated RGBA WebRTC transport handling in favor of H.264. - Implemented shared video frame encoding for Android using native H.264 encoder. - Improved error handling and logging for stream quality updates and encoding failures. - Updated SimDeck documentation to reflect changes in Android live viewing capabilities. * fix: improve android display streaming * fix: harden android and webrtc ci * fix: stabilize simulator integration retry * fix: retry webrtc reference screenshot in ci
1 parent d3cba2b commit de43ebd

33 files changed

Lines changed: 1803 additions & 3921 deletions

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ view inside the editor.
3939

4040
## Features
4141

42-
- Supports streaming both iOS simulators and Android emulators
42+
- Supports native H.264 streaming for both iOS simulators and Android emulators
4343
- Full simulator control & inspection using private iOS accessibility APIs and Android UIAutomator - available using `simdeck` CLI
4444
- Real-time screen `describe` command using accessibility view tree - available in token-efficient format for agents
4545
- Profiling built-in: CPU, memory, disk writes, network throughput, hang signals, and stack sampling
@@ -89,6 +89,9 @@ Normal service restarts preserve that token so paired clients stay connected.
8989
Use `simdeck service reset` only when you want to rotate the service token and
9090
restart the LaunchAgent.
9191
The service uses port 4310 unless you pass `-p` or `--port`.
92+
SimDeck-owned Android emulator boots use host GPU rendering by default; use
93+
`simdeck service restart --android-gpu auto` or
94+
`--android-gpu swiftshader_indirect` only as a machine-specific fallback.
9295
Use `simdeck service kill` when you want to stop every SimDeck service process,
9396
including services started from another checkout or installed binary.
9497

@@ -130,7 +133,6 @@ simdeck pasteboard get
130133
simdeck screenshot --output screen.png
131134
simdeck screenshot --with-bezel --output screen-bezel.png
132135
simdeck record --seconds 5 --output screen-recording.mp4
133-
simdeck stream --frames 120 > stream.h264
134136
simdeck describe
135137
simdeck describe --format agent --max-depth 4
136138
simdeck describe --format agent --max-depth 4 --interactive

docs/api/health.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Example:
2121
"serverKind": "launchAgent",
2222
"timestamp": 1714094761.234,
2323
"videoCodec": "auto",
24+
"androidGpu": "host",
2425
"lowLatency": false,
2526
"realtimeStream": true,
2627
"localStreamFps": 60,
@@ -46,6 +47,7 @@ Important fields:
4647
| `httpPort` | Port serving UI and API |
4748
| `serverKind` | `launchAgent` or `standalone` |
4849
| `videoCodec` | Requested codec mode: `auto`, `hardware`, or `software` |
50+
| `androidGpu` | Android emulator renderer mode for SimDeck-owned boots |
4951
| `streamQuality` | Active stream profile and limits |
5052
| `webRtc` | ICE settings the browser should use |
5153

@@ -90,7 +92,7 @@ Content-Type: application/json
9092
"clientId": "browser-ABC",
9193
"kind": "viewport",
9294
"udid": "9D7E5BB7-...",
93-
"codec": "h264",
95+
"codec": "video/H264/103",
9496
"decodedFps": 59.7,
9597
"droppedFps": 0.0,
9698
"latestRenderMs": 6.2

docs/api/rest.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,14 @@ Performance query parameters:
165165

166166
## Live video
167167

168-
| Method | Path | Purpose |
169-
| ------ | ------------------------------------- | -------------------------------------- |
170-
| `POST` | `/api/simulators/{udid}/webrtc/offer` | WebRTC offer/answer stream setup |
171-
| `GET` | `/api/simulators/{udid}/h264` | H.264 WebSocket fallback |
172-
| `GET` | `/api/simulators/{udid}/input` | Input WebSocket for fallback transport |
173-
| `GET` | `/api/simulators/{udid}/control` | Alias for input control WebSocket |
174-
| `POST` | `/api/simulators/{udid}/refresh` | Request a fresh frame or keyframe |
175-
176-
For normal clients, copy the browser behavior instead of hand-coding a raw decoder. The UI supports WebRTC first and H.264 WebSocket fallback.
168+
| Method | Path | Purpose |
169+
| ------ | ------------------------------------- | ------------------------------------- |
170+
| `POST` | `/api/simulators/{udid}/webrtc/offer` | WebRTC offer/answer stream setup |
171+
| `GET` | `/api/simulators/{udid}/input` | Input WebSocket fallback for controls |
172+
| `GET` | `/api/simulators/{udid}/control` | Alias for input control WebSocket |
173+
| `POST` | `/api/simulators/{udid}/refresh` | Request a fresh frame or keyframe |
174+
175+
For normal clients, copy the browser behavior instead of hand-coding a raw decoder. The UI uses the WebRTC offer endpoint for live video. Android emulator IDs use the same WebRTC endpoint; their H.264 frames are produced from the emulator `-share-vid` display surface, not screenshot polling.
177176

178177
Minimal WebRTC request:
179178

docs/cli/commands.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,6 @@ simdeck logs --seconds 30 --limit 200
207207
simdeck chrome-profile
208208
```
209209

210-
Diagnostic iOS H.264 stream:
211-
212-
```sh
213-
simdeck stream --frames 120 > stream.h264
214-
```
215-
216210
## Studio and providers
217211

218212
For hosted Studio workflows:

docs/cli/flags.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,19 @@ When `service restart` is run without `--port`, it preserves the installed
2626
LaunchAgent port or the current singleton service port before falling back to
2727
`4310`.
2828

29-
| Flag | Default | Notes |
30-
| ---------------------------- | -------------- | --------------------------------------------------------------------------------- |
31-
| `--port <port>` / `-p` | `4310` | HTTP port; `service restart` preserves the existing service port when omitted |
32-
| `--bind <ip>` | `127.0.0.1` | Use `0.0.0.0` or `::` for LAN access |
33-
| `--advertise-host <host>` | detected | Host printed for remote browsers |
34-
| `--client-root <path>` | bundled client | Static client directory |
35-
| `--video-codec <mode>` | `auto` | `auto`, `hardware`, or `software` |
36-
| `--stream-quality <profile>` | `full` | `full`, `balanced`, `economy`, `low`, `tiny`, `ci-software`, and related profiles |
37-
| `--local-stream-fps <fps>` | `60` | Local stream frame target |
38-
| `--low-latency` | off | Conservative software H.264 profile |
39-
| `--open` | off | Open the browser after starting the service |
40-
| `--autostart` / `-a` | off | Register the service as a macOS LaunchAgent |
29+
| Flag | Default | Notes |
30+
| ---------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------- |
31+
| `--port <port>` / `-p` | `4310` | HTTP port; `service restart` preserves the existing service port when omitted |
32+
| `--bind <ip>` | `127.0.0.1` | Use `0.0.0.0` or `::` for LAN access |
33+
| `--advertise-host <host>` | detected | Host printed for remote browsers |
34+
| `--client-root <path>` | bundled client | Static client directory |
35+
| `--video-codec <mode>` | `auto` | `auto`, `hardware`, or `software` |
36+
| `--android-gpu <mode>` | `host` | Android emulator renderer: `host`, `auto`, `software`, `lavapipe`, `swiftshader`, `swangle`, or `swiftshader_indirect` |
37+
| `--stream-quality <profile>` | `full` | `full`, `balanced`, `smooth`, `economy`, `low`, `tiny`, `ci-software`, and related profiles |
38+
| `--local-stream-fps <fps>` | `60` | Local stream frame target |
39+
| `--low-latency` | off | Conservative software H.264 profile |
40+
| `--open` | off | Open the browser after starting the service |
41+
| `--autostart` / `-a` | off | Register the service as a macOS LaunchAgent |
4142

4243
## `describe`
4344

docs/extensions/browser-client.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ Force a stream transport while debugging:
3838

3939
```text
4040
http://127.0.0.1:4310?stream=webrtc
41-
http://127.0.0.1:4310?stream=h264
4241
```
4342

4443
Use the default URL for normal operation.

docs/guide/architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ This is why a long-lived service feels faster than repeatedly calling lower-leve
2626

2727
## Video flow
2828

29-
The browser opens a live stream for the selected device. SimDeck sends fresh frames, drops stale ones when a client falls behind, and lets the browser request refreshes. The UI can use WebRTC or H.264-over-WebSocket fallback depending on browser support and network behavior.
29+
The browser opens a live WebRTC stream for the selected device. SimDeck sends fresh frames, drops stale ones when a client falls behind, and lets the browser request refreshes. iOS frames come from the native display bridge and are encoded on the Mac; Android frames come from the emulator `-share-vid` shared display surface and are encoded on the Mac.
3030

3131
Tune this from the user-facing controls or with:
3232

docs/guide/service.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,16 @@ LaunchAgent port or the current singleton service port before falling back to
6363
These options are accepted by `simdeck`, `service start`, `service restart`,
6464
`service on`, and `service reset`:
6565

66-
| Flag | Default | Use it when |
67-
| ---------------------------- | ----------- | ------------------------------------------ |
68-
| `--port <port>` / `-p` | `4310` | You want a specific service port |
69-
| `--bind <ip>` | `127.0.0.1` | You need LAN access with `0.0.0.0` or `::` |
70-
| `--advertise-host <host>` | detected | Remote browsers need a specific host or IP |
71-
| `--video-codec <mode>` | `auto` | You need to force encoder behavior |
72-
| `--stream-quality <profile>` | `full` | You want lower CPU or bandwidth use |
73-
| `--local-stream-fps <fps>` | `60` | You want a different local stream target |
74-
| `--client-root <path>` | bundled UI | You are serving a custom static client |
66+
| Flag | Default | Use it when |
67+
| ---------------------------- | ----------- | ------------------------------------------------- |
68+
| `--port <port>` / `-p` | `4310` | You want a specific service port |
69+
| `--bind <ip>` | `127.0.0.1` | You need LAN access with `0.0.0.0` or `::` |
70+
| `--advertise-host <host>` | detected | Remote browsers need a specific host or IP |
71+
| `--video-codec <mode>` | `auto` | You need to force encoder behavior |
72+
| `--android-gpu <mode>` | `host` | You need to change Android emulator GPU rendering |
73+
| `--stream-quality <profile>` | `full` | You want lower CPU or bandwidth use |
74+
| `--local-stream-fps <fps>` | `60` | You want a different local stream target |
75+
| `--client-root <path>` | bundled UI | You are serving a custom static client |
7576

7677
## Restart CoreSimulator
7778

docs/guide/troubleshooting.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,14 @@ curl http://127.0.0.1:4310/api/metrics
137137

138138
If `frames_dropped_server` keeps climbing, the client or network cannot keep up. Move closer to the host, reduce quality, or switch to software encoding.
139139

140+
For Android emulator streams, SimDeck uses the emulator `-share-vid` shared display surface. If Android video never starts, confirm `adb devices` shows the emulator as `device`, that it has fully booted, and that externally launched emulators were started with `-share-vid`. SimDeck-owned Android boots add the flag automatically and default to `--android-gpu host`; try `simdeck service restart --android-gpu auto` or `--android-gpu swiftshader_indirect` only when host rendering is unstable.
141+
140142
### Browser cannot establish WebRTC
141143

142-
Force the H.264 WebSocket fallback while testing:
144+
Use `?stream=webrtc` while testing to make transport selection explicit:
143145

144146
```text
145-
http://127.0.0.1:4310?stream=h264
147+
http://127.0.0.1:4310?stream=webrtc
146148
```
147149

148150
For routed remote sessions, configure TURN as described in [Video and streaming](/guide/video#remote-browsers).

docs/guide/video.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
# Video and streaming
22

3-
SimDeck streams live device video to the browser. Local sessions default to high quality. Remote or constrained sessions can trade detail for lower CPU and latency.
3+
SimDeck streams live device video to the browser. Local sessions default to full-resolution 60 fps. Remote or constrained sessions can trade detail for lower CPU and latency.
44

55
iOS simulator H.264 uses VideoToolbox for hardware encoding and x264 for software encoding.
6+
Android emulator H.264 uses the emulator `-share-vid` display surface. SimDeck reads BGRA frames from the `videmulator<console-port>` shared memory region and encodes them on the Mac, so normal Android live video stays on the native shared display path.
67

78
## When encoding runs
89

9-
SimDeck starts encoding when a browser stream needs H.264 frames. The server
10-
requests an initial keyframe to answer the WebRTC or H.264 WebSocket viewer,
11-
then keeps a shared refresh pump active while frame subscribers exist.
10+
SimDeck starts encoding when a browser stream needs H.264 frames. For iOS, the
11+
server requests an initial keyframe to answer the WebRTC viewer, then keeps a
12+
shared refresh pump active while frame subscribers exist.
13+
For Android, SimDeck starts emulators with `-share-vid`, maps the shared display
14+
region, and feeds changed BGRA frames into the native host H.264 encoder.
15+
SimDeck-owned Android boots also default to `-gpu host`, matching the native
16+
emulator app's accelerated renderer while staying in headless shared-video mode.
1217

1318
The browser reports whether the page and stream canvas are foreground. When all
1419
known viewers are hidden or the last frame subscriber disconnects, the native
@@ -35,7 +40,8 @@ Common profiles:
3540

3641
| Profile | Use it for |
3742
| ------------- | --------------------------------------- |
38-
| `full` | Local browser on a fast Mac |
43+
| `full` | Default local full-resolution 60 fps |
44+
| `smooth` | Full-size 60 fps with lower bitrate |
3945
| `balanced` | Good local quality with less bandwidth |
4046
| `economy` | Remote browser or busy machine |
4147
| `low` | Slower Wi-Fi or shared hosts |
@@ -44,6 +50,18 @@ Common profiles:
4450

4551
The browser also has stream controls for transport, resolution, FPS, and refresh.
4652

53+
## Pick an Android GPU mode
54+
55+
SimDeck-owned Android emulator boots use host GPU acceleration by default:
56+
57+
```sh
58+
simdeck service restart --android-gpu host
59+
```
60+
61+
Use `auto` to let the Android emulator choose the renderer. Use
62+
`swiftshader_indirect`, `swiftshader`, `software`, `lavapipe`, or `swangle` only
63+
when host rendering is unstable on a specific machine.
64+
4765
## Pick a codec
4866

4967
```sh
@@ -58,6 +76,12 @@ simdeck service restart --video-codec software
5876
| `hardware` | Dedicated local machines where VideoToolbox hardware H.264 is reliable. |
5977
| `software` | x264 software H.264 for CI, screen recording conflicts, or hardware encoder stalls. |
6078

79+
The codec setting controls simulator host encoding. Android emulator streams use
80+
the same service codec by default for shared display frames; set
81+
`SIMDECK_ANDROID_VIDEO_CODEC=auto`, `hardware`, or `software` before starting the
82+
service only when you need an Android-specific encoder override. Stream quality
83+
controls the encoded Android frame size.
84+
6185
When multiple simulator streams run at the same time, `auto` keeps one active
6286
stream on the hardware encoder path and routes additional active auto streams to
6387
software encoding. This avoids saturating the shared VideoToolbox hardware
@@ -69,15 +93,14 @@ For very constrained software sessions:
6993
simdeck service restart --video-codec software --low-latency
7094
```
7195

72-
## WebRTC and fallback
96+
## WebRTC
7397

74-
The browser tries WebRTC first. If WebRTC cannot render a frame, the UI can fall back to H.264 over WebSocket when the browser supports WebCodecs.
98+
The browser uses WebRTC for live video. SimDeck no longer exposes a separate H.264 WebSocket video transport.
7599

76100
Force a mode while debugging:
77101

78102
```text
79103
http://127.0.0.1:4310?stream=webrtc
80-
http://127.0.0.1:4310?stream=h264
81104
```
82105

83106
## Remote browsers

0 commit comments

Comments
 (0)