Skip to main content
Glama
SCRCPY_STREAMING.md7.01 kB
# Scrcpy-Based Fast Frame Streaming ## Overview The Android MCP Server now supports **scrcpy-based frame streaming** for ultra-fast screen capture, which is ideal for agents/models that need to check the screen quickly without latency. ## Why Scrcpy? Scrcpy is significantly faster than ADB screencap for several reasons: 1. **Direct ADB tunneling** - Uses H.264 hardware encoding on the device 2. **Lower overhead** - No PNG compression/decompression cycle 3. **Streaming support** - Can maintain persistent connection for frame polling 4. **30fps capability** - Can capture frames at 30fps if needed 5. **Adjustable bitrate** - Trade-off between quality and speed ### Performance Comparison - **ADB screencap**: 500-1500ms per frame (PNG encode/decode) - **Scrcpy single frame**: 100-300ms per frame (H.264 hardware encoded) - **Scrcpy stream polling**: <50ms per frame (pre-buffered data) ## Installation ### Linux ```bash # Ubuntu/Debian sudo apt install scrcpy # Arch sudo pacman -S scrcpy # Or from source git clone https://github.com/Genymobile/scrcpy.git cd scrcpy && ./install_release.sh ``` ### macOS ```bash # Homebrew brew install scrcpy # Or MacPorts sudo port install scrcpy ``` ### Windows The MCP server will attempt to auto-download scrcpy v2.4 from GitHub releases. If auto-download fails, you can: ```powershell # Using chocolatey choco install scrcpy # Or download manually from: # https://github.com/Genymobile/scrcpy/releases/download/v2.4/scrcpy-2.4-win64-v15.zip ``` ## Usage ### Option 1: Continuous Streaming (Fastest for Agents) For the fastest frame access when checking screen state repeatedly: ```javascript // Start streaming await agent.use_mcp_tool("android-mcp-server", "android_start_scrcpy_stream", { deviceSerial: "emulator-5554" }); // Get frames instantly (no latency - pre-buffered data) const frame1 = await agent.use_mcp_tool( "android-mcp-server", "android_get_latest_frame", {} ); // Perform action await agent.use_mcp_tool("android-mcp-server", "android_touch", { x: 500, y: 1000, deviceSerial: "emulator-5554" }); // Get updated frame instantly const frame2 = await agent.use_mcp_tool( "android-mcp-server", "android_get_latest_frame", {} ); // Stop streaming when done await agent.use_mcp_tool("android-mcp-server", "android_stop_scrcpy_stream", {}); ``` **Latency Profile:** - Stream start: ~2000ms (one-time) - Frame retrieval: <50ms (polling pre-buffered stream) - Total cycle: ~50ms per screen check ### Option 2: Single Frame Capture (Good Balance) For one-off frame captures without starting a persistent stream: ```javascript const frame = await agent.use_mcp_tool( "android-mcp-server", "android_capture_frame_scrcpy", { outputPath: "/tmp/screen.png", // optional deviceSerial: "emulator-5554" } ); ``` **Latency Profile:** - Single capture: 100-300ms - Includes scrcpy startup time - Good for periodic checks, not continuous polling ### Option 3: Fallback to ADB Screencap If scrcpy is not available, both tools automatically fall back to ADB screencap: ```javascript // Works even without scrcpy - will use ADB screencap const frame = await agent.use_mcp_tool( "android-mcp-server", "android_capture_frame_scrcpy", {} ); ``` ## MCP Tools ### `android_start_scrcpy_stream` Start persistent scrcpy streaming for rapid frame polling. **Parameters:** - `deviceSerial` (optional): Target device serial **Returns:** Success message **Performance:** 2000ms first time, establishes persistent connection **Configuration:** - Max FPS: 30fps - Codec: H.264 (hardware accelerated) - Bitrate: 5Mbps - Display: None (no window on host) ### `android_get_latest_frame` Retrieve the latest frame from active stream (instant access). **Parameters:** None **Returns:** Base64-encoded H.264 frame or error if stream not active **Performance:** <50ms **Note:** Returns `null` if no stream is running ### `android_stop_scrcpy_stream` Stop the streaming session and clean up resources. **Parameters:** None **Returns:** Success message **Performance:** Immediate ### `android_capture_frame_scrcpy` Single-frame capture using scrcpy (faster than ADB). **Parameters:** - `outputPath` (optional): Save to file path - `deviceSerial` (optional): Target device **Returns:** File path (if outputPath given) or base64-encoded PNG **Performance:** 100-300ms **Fallback:** Automatically uses ADB screencap if scrcpy unavailable ## Architecture ### Streaming Pipeline ``` Device (H.264 Video Stream) ↓ Scrcpy Process (stdout pipe) ↓ Node.js Buffer (latest frame stored) ↓ Agent/Model (instant access via getLatestFrame) ``` ### Frame Storage - Only the latest frame is kept in memory - Previous frames are discarded to avoid memory buildup - Stream runs at 30fps but you can poll at any rate ## Optimization Tips ### For Fast Agent Decision-Making 1. **Start stream at app launch:** ```javascript await startApp(); await startScrcpyStream(); ``` 2. **Poll frames in decision loop:** ```javascript while (running) { const frame = await getLatestFrame(); const decision = analyzeFrame(frame); await executeAction(decision); // Ultra-low latency cycle } ``` 3. **Stop stream when done:** ```javascript await stopScrcpyStream(); ``` ### For Periodic Monitoring Use single frame capture instead: ```javascript // Check every 5 seconds while (monitoring) { const frame = await captureFrameScrcpy(); checkForErrors(frame); await sleep(5000); } ``` ## Troubleshooting ### "scrcpy not found" Error 1. Install scrcpy manually (see Installation above) 2. Ensure it's in PATH: `scrcpy --version` 3. On Windows, the MCP server will try to auto-download ### Frame Quality Issues - Increase bitrate in adb-wrapper.ts (current: 5Mbps) - Use H.264 profile adjustments if needed - Check network bandwidth for remote devices ### Streaming Stops - Device may have gone to sleep (enable "Stay Awake" in developer settings) - ADB connection may have dropped (reconnect) - Scrcpy process crashed (check error logs) ### High CPU Usage - Reduce stream FPS from 30 to 10-15 - Decrease poll rate if using getLatestFrame - Disable stream when not needed ## Configuration Edit `src/adb-wrapper.ts` to adjust scrcpy parameters: ```typescript // In startScrcpyStream() method: spawn(this.scrcpyPath, [ '--max-fps=30', // Adjust FPS '--video-bit-rate=5M', // Adjust quality '--video-codec=h264', // Can use vp9 for better compression // Add more options as needed ]) ``` Available scrcpy options: - `--max-fps=N`: Frame rate cap - `--video-bit-rate=Nk/M`: Bitrate (k=kbps, M=Mbps) - `--encoder=name`: Hardware encoder selection - `--power-off-on-close`: Power off device on close ## References - [Scrcpy GitHub](https://github.com/Genymobile/scrcpy) - [Scrcpy Manual](https://github.com/Genymobile/scrcpy/blob/master/README.md) - [H.264 Codec Info](https://en.wikipedia.org/wiki/H.264)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jduartedj/android-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server