Skip to content

feat: stream Android emulators on Windows and Linux with OpenH264 - #86

Open
NathanWalker wants to merge 9 commits into
mainfrom
fix/windows-live-video-messaging
Open

feat: stream Android emulators on Windows and Linux with OpenH264#86
NathanWalker wants to merge 9 commits into
mainfrom
fix/windows-live-video-messaging

Conversation

@NathanWalker

@NathanWalker NathanWalker commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Android emulator live streaming now works on Windows and Linux. Those builds link native_stubs.c instead of the macOS simulator bridge, so the Android WebRTC source had no H.264 encoder and every stream failed with the stub error (rendered as raw JSON in the browser). Frame capture (emulator gRPC screenshots) and the WebRTC transport were already cross-platform Rust; only the encoder was missing.

Encoder (non-macOS)

  • New packages/server/src/transport/software_h264.rs: Cisco OpenH264 via the openh264 crate, compiled from source (BSD, no system deps, supported on x86_64-pc-windows-gnu/msvc and Linux). Emits Annex B baseline H.264, which the existing packetizer and browsers already accept.
  • Bitrate mirrors the macOS budget (bits per pixel with a minimum), keyframes honor RTCP PLI/FIR requests, the encoder rebuilds on size or quality changes, and stats surface under androidEncoders[].encoder.native.
  • The Android WebRTC source is cfg-gated: macOS keeps the native VideoToolbox/x264 path behind the C ABI; other targets use the software encoder. Frame publishing is shared.

Capability reporting and messaging

  • platform.rs now describes iOS simulator support rather than "live video". liveVideo in /api/health and /api/stream-quality reports supported: true, encoder (native or openh264), androidEmulator: true, iosSimulator (macOS only) and iosSimulatorReason elsewhere.
  • WebRTC offers for iOS simulators on non-macOS answer 501 with that reason (new AppError::Unsupported). Android offers proceed.
  • The CLI prints a Live video: note on Windows/Linux; the browser unwraps JSON error bodies instead of showing raw JSON, and still pauses with a reason if a server ever reports supported: false.

CI and docs

  • New rust-non-macos job runs cargo clippy -D warnings and cargo test on Ubuntu and Windows, since the macOS job compiles out both the OpenH264 module and the native stubs.
  • Per-platform encoder table in the video guide, install-guide platform support, health/REST field docs, troubleshooting entries, AGENTS pointer.

Test plan

  • npm run --prefix packages/client typecheck and npm run --prefix packages/client test (104 tests)
  • CI: rust (macOS fmt/clippy/test) and the new rust-non-macos job (Ubuntu + Windows clippy/test, including the OpenH264 encoder tests) are green as of 7fb0e39. Release run 34411749072 built all four native artifacts (macOS arm64/x64, Linux, Windows) with OpenH264.
  • Windows: simdeck prints the Live video: note, /api/health reports liveVideo.encoder: "openh264", boot an Android emulator and confirm the browser shows live video with touch input; /api/metrics shows androidEncoders[].encoder.native.outputFrames increasing.
  • macOS: Android and iOS streams unchanged, liveVideo.encoder: "native".
  • Cargo.lock gains the openh264 entries via the release version-bump commit (the workflow stages it).

Also fixed along the way: pre-existing lints that the new clippy 1.98 and the first non-macOS clippy run surfaced (chunks_exact, needless_return, unused imports, an unread fd, result_large_err in generated tonic code), and two /bin/sh log-stream tests are now Unix-only.

Follow-up worth considering: install nasm on the release runners for OpenH264's assembly paths (up to ~3x faster encoding); left out to keep the release build unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_013K2GgwpF2vLSgz4otg8tmT

Non-macOS builds link native_stubs.c instead of the macOS native bridge, so
neither iOS simulator nor Android emulator H.264 streaming can work there.
The browser previously showed the raw JSON stub error in the stream area and
kept retrying WebRTC offers that could never succeed.

Server:
- Add platform.rs as the single source of truth for the live-video
  capability, its user-facing reason, and the CLI note.
- Report `hostOs` and a `liveVideo` block from /api/health and
  /api/stream-quality.
- Reject WebRTC offers early with 501 and the platform explanation via a new
  AppError::Unsupported variant.
- Print a "Live video:" note after the service URLs on non-macOS hosts.
- Share one clear message across the H.264 encoder stubs; drop the unused
  WIP helper and the duplicated #[test] attribute.

Client:
- Parse `{"error": ...}` bodies from failed offer and stream-config posts.
- Read `liveVideo` from the stream-quality response, pause the live stream,
  and show the reason in the viewport instead of the reconnect loop.
- Disable the stream transport, encoder, frame rate, and resolution controls
  with a "Requires macOS" note when the server cannot stream.

Docs: platform support table, health/REST field docs, video guide, and a
troubleshooting entry for the message.
Non-macOS builds link native_stubs.c instead of the macOS simulator bridge,
so the Android emulator WebRTC source had no H.264 encoder and every stream
failed with the stub error. Frame capture (emulator gRPC screenshots) and the
WebRTC transport were already cross-platform Rust; only the encoder was
missing.

Server:
- Add transport/software_h264.rs: an OpenH264 encoder (openh264 crate,
  compiled from source, BSD) that turns RGBA/BGRA emulator frames into
  Annex B baseline H.264. Bitrate follows the macOS budget (bits per pixel
  with a minimum), keyframes honor RTCP requests, and the encoder rebuilds
  on size or quality changes. Unit tests cover the budget math, frame
  validation, keyframe forcing, and reinitialization.
- Route the Android WebRTC source through it on non-macOS builds; macOS
  keeps the native VideoToolbox/x264 path behind the C ABI. Frame
  publishing is shared between both paths.
- Reframe platform.rs around iOS simulator support: liveVideo now reports
  supported: true everywhere with encoder ("native" or "openh264"),
  androidEmulator, iosSimulator, and an iosSimulatorReason off macOS.
  WebRTC offers for iOS simulators on non-macOS answer 501; Android offers
  proceed.
- CLI banner note now says Android streams with software H.264 and iOS
  simulators require macOS.

CI: add a Linux and Windows cargo clippy/test job so the OpenH264 module and
the native stubs are linted and tested where they are actually compiled.

Docs: per-platform encoder table, health/REST field docs, troubleshooting
entries for the iOS message and slow software encoding, AGENTS pointer.

Cargo.lock is not updated here (no Rust toolchain on the authoring machine);
the first cargo build adds the openh264 entries.
@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 8ec977e4-3634-48f7-b636-4ce0c4f74050

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 9, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
simdeckciproxy 558220d Commit Preview URL

Branch Preview URL
Sep 09 2026, 11:49 PM

CI's first run of the Linux and Windows clippy/test job surfaced:
- rustfmt layout in software_h264.rs
- clippy manual_is_multiple_of in the frame validator
- an unused platform constant
- FFI H.264 encoder declarations that are now macOS-only
- test constructors that referenced the macOS-only encoder fields
- pre-existing non-macOS lints that had never run: needless_return and an
  unread fd in the POSIX shared-video stub, an unused c_void import in
  performance.rs, parse_io_platform_uuid dead off macOS, and
  result_large_err in the generated tonic client
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 9, 2026

Copy link
Copy Markdown

Deploying simdeck with  Cloudflare Pages  Cloudflare Pages

Latest commit: 558220d
Status: ✅  Deploy successful!
Preview URL: https://167790fb.simdeck.pages.dev
Branch Preview URL: https://fix-windows-live-video-messa.simdeck.pages.dev

View logs

Pre-existing test code on main; the newer stable clippy flags
chunks_exact with a constant size, which fails all three Rust jobs.
OpenH264's screen-content mode emits an IDR when the whole frame changes
color, which the Linux test run showed; only the forced keyframe and the
Annex B framing are guaranteed.
Pre-existing tests that spawn /bin/sh; the new Windows CI job cannot run
them. The OpenH264 encoder tests and the other 275 tests pass there.
Windows clippy flagged the imports and new_for_tests helper that only the
/bin/sh tests use.
The native artifacts were compiled in a job that ran before the release
job bumped package.json and Cargo.toml, so simdeck@0.1.35 shipped
binaries whose `--version` still said 0.1.34.

- Resolve the release version in a new `resolve-version` job that runs
  first and feeds both the native builds and the release job.
- Sync packages/server/Cargo.toml (and Cargo.lock) to that version in
  each native build job before `npm run build:cli`.
- Run every built binary with `--version` and fail the job if it does
  not report the release version; do the same for the universal macOS
  binary in the release job before committing, tagging, or publishing.
- Apply the resolved version verbatim in the release job instead of
  recomputing it, and guard `bump=current` against a mismatch.
- Cover the new ordering in scripts/github-actions.test.mjs and fix the
  pre-existing `test -f "$artifact"` assertion, which expected
  backslash-escaped quotes and always failed.
- Document the flow in CONTRIBUTING.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant