# scrcpy Administrator & Developer Guide

scrcpy (screen copy) is a high-performance client-server application that displays and controls Android devices from a desktop computer. The client is a native C application rendering through SDL2 and FFmpeg; the server is an Android Java application injected onto the device via adb. All communication flows over adb connections (USB or TCP/IP), requiring no root access and no app installation on the device.

This guide covers architecture, build system, configuration, deployment, security considerations, and developer internals for scrcpy v3.3.4 (commit `247a37d`).

---

## Table of Contents

- [Architecture Overview](#architecture-overview)
- [System Requirements](#system-requirements)
- [Configuration Reference](#configuration-reference)
- [Deployment & Scaling](#deployment--scaling)
- [Operations](#operations)
- [Performance Tuning](#performance-tuning)
- [Security](#security)
- [Developer Internals](#developer-internals)
- [Integration Points](#integration-points)

---

## Architecture Overview

scrcpy operates as a two-component system: a host-side C client and a device-side Java server. The client pushes the server JAR to the device, launches it via adb shell, and establishes socket connections for video, audio, and control streams.

```
┌─────────────────────────────────────────────────────────────────┐
│                        HOST (Desktop)                           │
│                                                                 │
│  main.c ──► cli.c (parse args) ──► scrcpy.c (orchestration)    │
│                                        │                        │
│            ┌───────────────────────────┼──────────────────┐     │
│            │                          │                   │     │
│       server.c               demuxer.c (x2)        controller.c │
│    (push JAR, launch,     (video + audio)        (control msgs) │
│     establish sockets)         │                       │        │
│            │              decoder.c              control_msg.c   │
│            │                  │                       │         │
│            │         ┌───────┴────────┐               │        │
│            │         │                │               │        │
│            │    display.c /      recorder.c           │        │
│            │    screen.c (SDL)   (FFmpeg mux)         │        │
│            │         │                                │        │
│            │    audio_player.c                        │        │
│            │    (SDL audio)                           │        │
│            │                                          │        │
│            │    ┌──────────────────────┐               │        │
│            │    │  HID subsystem       │               │        │
│            │    │  hid/ (kb,mouse,pad) │               │        │
│            │    │  uhid/ (Linux UHID)  │               │        │
│            │    │  usb/ (AOAv2)        │               │        │
│            │    └──────────────────────┘               │        │
│            │                                          │        │
│            │    v4l2_sink.c (Linux)                    │        │
│            │    file_pusher.c (drag & drop)            │        │
└────────────┼──────────────────────────────────────────┼────────┘
             │              ADB (USB / TCP)              │
             │         ┌────────────────────┐            │
             ▼         ▼                    ▼            ▼
┌─────────────────────────────────────────────────────────────────┐
│                      DEVICE (Android)                           │
│                                                                 │
│  /data/local/tmp/scrcpy-server.jar                              │
│                                                                 │
│  Server.java (entry point)                                      │
│       │                                                         │
│       ├── Options.java (parse server-side options)               │
│       │                                                         │
│       ├── video/                                                │
│       │    ScreenCapture / CameraCapture / NewDisplayCapture    │
│       │    └── SurfaceEncoder ──► Streamer ──► socket           │
│       │                                                         │
│       ├── audio/                                                │
│       │    AudioDirectCapture / AudioPlaybackCapture             │
│       │    └── AudioEncoder / AudioRawRecorder ──► socket       │
│       │                                                         │
│       ├── control/                                              │
│       │    ControlMessageReader ──► Controller                  │
│       │    └── injects via InputManager, ClipboardManager, etc. │
│       │                                                         │
│       └── wrappers/ (hidden Android API access via reflection)  │
│            ActivityManager, ClipboardManager, DisplayControl,   │
│            DisplayManager, InputManager, PowerManager,          │
│            ServiceManager, StatusBarManager, SurfaceControl,    │
│            WindowManager, ContentProvider                       │
└─────────────────────────────────────────────────────────────────┘
```

### Connection establishment

1. The client pushes `scrcpy-server.jar` to `/data/local/tmp/scrcpy-server.jar` on the device via `adb push`.
2. The client starts the server via `adb shell` with appropriate arguments.
3. By default, the client uses `adb reverse` to make the device connect back to the host (the client listens, the server connects). The `--force-adb-forward` flag reverses this so the client connects to a forwarded port instead.
4. The socket name prefix used is `scrcpy_`.
5. Separate socket connections are established for video, audio, and control streams.

### Data flow summary

| Stream | Direction | Protocol |
|--------|-----------|----------|
| Video | Device → Host | Encoded frames (H.264/H.265/AV1) via packet stream |
| Audio | Device → Host | Encoded frames (Opus/AAC/FLAC/RAW) via packet stream |
| Control | Host → Device | Serialized control messages (touch, key, clipboard, etc.) |
| Device messages | Device → Host | Clipboard content, ACKs |

> [!NOTE]
> scrcpy does not install anything on the device. The server JAR is pushed to a temporary location and executed via `adb shell`. No permanent modifications are made.

---

## System Requirements

### Host requirements

| Component | Minimum | Notes |
|-----------|---------|-------|
| OS | Linux, macOS, Windows | V4L2 sink is Linux-only; OTG/UHID requires Linux |
| ADB | Any recent version | Must be in PATH or set via `ADB` env var |
| SDL2 | 2.x | Display and audio output |
| FFmpeg | libavcodec, libavformat, libavutil, libswresample | Video/audio decoding and recording |
| libusb | Optional | Required for AOAv2 (USB HID) and OTG mode |
| Meson + Ninja | Build-time only | Client build system |

### Device requirements

| Component | Requirement |
|-----------|-------------|
| Android | API 21+ (Android 5.0 Lollipop) |
| USB debugging | Must be enabled in Developer Options |
| Audio capture | Android 11+ (API 30) for audio forwarding |

### Build-time requirements

| Component | Purpose |
|-----------|---------|
| Meson | Client build system |
| Ninja | Build backend |
| C compiler | GCC or Clang for client |
| pkg-config | Dependency resolution |
| Android SDK | compileSdk 36 for server |
| Gradle | Server build (or use `build_without_gradle.sh`) |
| Java JDK | Server compilation |

> [!IMPORTANT]
> The server targets `compileSdk: 36`, `minSdk: 21`, `targetSdk: 36`. The server has zero runtime dependencies beyond the Android SDK itself; `junit:junit:4.13.2` is used for tests only.

---

## Configuration Reference

### CLI options (complete list)

All options are passed to the `scrcpy` client binary. The client translates them into server arguments where applicable.

#### Video options

| Option | Default | Description |
|--------|---------|-------------|
| `--video-codec` | `h264` | Video codec: `h264`, `h265`, or `av1` |
| `--video-bit-rate` | `8M` | Video encoding bit rate (e.g., `8M`, `2M`) |
| `--video-source` | `display` | Video source: `display` or `camera` |
| `--video-buffer` | `0` (ms) | Video buffering delay for jitter compensation |
| `--max-size` | `0` (unlimited) | Limit both dimensions to this value (preserving aspect ratio) |
| `--max-fps` | (none) | Maximum framerate cap |
| `--capture-orientation` | `0` | Capture orientation in degrees |
| `--display-orientation` | `0` | Display orientation in degrees |
| `--record-orientation` | `0` | Recording orientation in degrees |
| `--display-id` | `0` | Android display ID to mirror |
| `--no-video` | (flag) | Disable video forwarding |
| `--no-video-playback` | (flag) | Disable video playback (but still allow recording) |
| `--video-codec-options` | (none) | Codec-specific options (key=value, comma-separated) |
| `--video-encoder` | (auto) | Force a specific video encoder |

#### Audio options

| Option | Default | Description |
|--------|---------|-------------|
| `--audio-codec` | `opus` | Audio codec: `opus`, `aac`, `flac`, or `raw` |
| `--audio-bit-rate` | `128K` | Audio encoding bit rate |
| `--audio-source` | `output` | Audio source: `output` (device playback) or `mic` |
| `--audio-buffer` | `50` (ms) | Audio buffering delay |
| `--audio-output-buffer` | `5` (ms) | SDL audio output buffer size |
| `--no-audio` | (flag) | Disable audio forwarding |
| `--no-audio-playback` | (flag) | Disable audio playback (but still allow recording) |
| `--audio-codec-options` | (none) | Audio codec-specific options |
| `--audio-encoder` | (auto) | Force a specific audio encoder |

#### Recording options

| Option | Default | Description |
|--------|---------|-------------|
| `--record` / `-r` | (none) | Record to file (format inferred from extension) |
| `--record-format` | (auto) | Force recording format: `mp4`, `mkv`, `m4a`, `mka`, `opus`, `aac`, `flac`, `wav` |
| `--no-playback` | (flag) | Disable both video and audio playback (recording-only mode) |

#### Input options

| Option | Default | Description |
|--------|---------|-------------|
| `--keyboard` | (auto) | Keyboard input mode: `sdk`, `uhid`, `aoa`, `disabled` |
| `--mouse` | (auto) | Mouse input mode: `sdk`, `uhid`, `aoa`, `disabled` |
| `--gamepad` | (auto) | Gamepad input mode: `uhid`, `aoa`, `disabled` |
| `--mouse-bind` | `bhsn:++++` (SDK) / `++++:bhsn` (AOA/UHID) | Mouse button bindings |
| `--shortcut-mod` | `lalt,lsuper` | Shortcut modifier key(s) |
| `--no-control` | (flag) | Disable device control (mirror only) |
| `--no-key-repeat` | (flag) | Do not forward repeated key events |

#### Connection options

| Option | Default | Description |
|--------|---------|-------------|
| `--serial` / `-s` | (auto) | Device serial number (or `ANDROID_SERIAL` env) |
| `--tcpip` | (none) | Connect via TCP/IP (optionally specify `host[:port]`) |
| `--force-adb-forward` | (flag) | Use `adb forward` instead of `adb reverse` |
| `--tunnel-host` | (auto) | Host for tunnel setup |
| `--tunnel-port` | (auto) | Port for tunnel setup |

#### Display options

| Option | Default | Description |
|--------|---------|-------------|
| `--window-title` | (auto) | Custom window title |
| `--window-x` | (auto) | Initial window X position |
| `--window-y` | (auto) | Initial window Y position |
| `--window-width` | (auto) | Initial window width |
| `--window-height` | (auto) | Initial window height |
| `--window-borderless` | (flag) | Remove window decorations |
| `--always-on-top` | (flag) | Keep window above others |
| `--fullscreen` / `-f` | (flag) | Start in fullscreen |
| `--no-window` | (flag) | Do not display a window |
| `--rotation` | (auto) | Window rotation |

#### Device control options

| Option | Default | Description |
|--------|---------|-------------|
| `--turn-screen-off` / `-S` | (flag) | Turn device screen off on start |
| `--stay-awake` / `-w` | (flag) | Keep device awake while connected |
| `--show-touches` / `-t` | (flag) | Enable "Show touches" on device |
| `--power-off-on-close` | (flag) | Turn off device screen on close |
| `--no-power-on` | (flag) | Do not wake device on start |
| `--no-cleanup` | (flag) | Do not restore device state on exit |
| `--push-target` | `/sdcard/Download/` | Target directory for file pushes (drag & drop) |
| `--new-display` | (none) | Create a new virtual display |

#### Miscellaneous options

| Option | Default | Description |
|--------|---------|-------------|
| `--verbosity` / `-V` | `info` (`debug` in debug builds) | Log level: `verbose`, `debug`, `info`, `warn`, `error` |
| `--version` / `-v` | (flag) | Print version |
| `--help` / `-h` | (flag) | Print help |
| `--pause-on-exit` | `false` | Pause terminal on exit: `true`, `false`, `if-error` |
| `--prefer-text` | (flag) | Prefer text injection for input |
| `--raw-key-events` | (flag) | Inject raw key events |
| `--forward-all-clicks` | (flag) | Forward right and middle clicks to device |
| `--legacy-paste` | (flag) | Use legacy paste method |
| `--no-downsize-on-error` | (flag) | Do not retry with smaller size on encoding error |
| `--crop` | (none) | Crop the device screen (format: `width:height:x:y`) |
| `--render-driver` | (auto) | SDL render driver name |
| `--list-encoders` | (flag) | List available encoders |
| `--list-displays` | (flag) | List available displays |
| `--list-cameras` | (flag) | List available cameras |
| `--list-camera-sizes` | (flag) | List available camera sizes |
| `--otg` | (flag) | OTG mode (USB HID only, no mirroring) |
| `--v4l2-sink` | (none) | V4L2 device path for video output (Linux only) |
| `--v4l2-buffer` | `0` (ms) | V4L2 output buffer delay |
| `--time-limit` | (none) | Maximum session duration |
| `--camera-id` | (none) | Camera ID to use |
| `--camera-size` | (auto) | Camera capture size |
| `--camera-facing` | (none) | Camera facing: `front`, `back`, `external` |
| `--camera-ar` | (none) | Camera aspect ratio |
| `--camera-fps` | (none) | Camera capture framerate |
| `--camera-high-speed` | (flag) | Use high-speed camera capture |

### Environment variables

| Variable | Purpose |
|----------|---------|
| `ADB` | Path to the `adb` binary. Overrides the default `adb` found in PATH. |
| `ANDROID_SERIAL` | Device serial number. Equivalent to `--serial` / `-s`. |
| `SCRCPY_SERVER_PATH` | Path to the `scrcpy-server` JAR on the host. Overrides the default installed location (`PREFIX/share/scrcpy/scrcpy-server`). |
| `SCRCPY_ICON_PATH` | Path to a custom window icon file. |

### Exit codes

| Code | Meaning |
|------|---------|
| `0` | Normal exit |
| `1` | Start failure (could not initialize, connect, or launch server) |
| `2` | Device disconnected during session |

### Default ADB port

The default ADB port for TCP/IP connections is `5555`.

---

## Deployment & Scaling

### Building from source

#### Building the server

The server is an Android Java application built with Gradle:

```bash
# Standard Gradle build
cd server
./gradlew assembleRelease
```

The server can also be built without Gradle using the Android SDK directly:

```bash
cd server
./build_without_gradle.sh
```

> [!NOTE]
> The server build produces a JAR file. ProGuard rules are defined in `server/proguard-rules.pro` for release builds.

#### Building the client

The client uses Meson and Ninja:

```bash
# Configure
meson setup build --buildtype=release

# Build
ninja -C build

# Install
sudo ninja -C build install
```

Build options are defined in `meson_options.txt` and can be set during configuration:

```bash
meson setup build --buildtype=release -Doption_name=value
```

#### Using install_release.sh

The `install_release.sh` script automates the full process: downloads a prebuilt server binary, builds the client with Meson and Ninja, and installs everything:

```bash
./install_release.sh
```

### Cross-compilation

Cross-compilation configurations are provided for Windows targets:

| File | Target |
|------|--------|
| `cross_win32.txt` | Windows 32-bit |
| `cross_win64.txt` | Windows 64-bit |

Usage:

```bash
meson setup build-win64 --cross-file cross_win64.txt --buildtype=release
ninja -C build-win64
```

> [!WARNING]
> Cross-compilation requires the appropriate cross-compiler toolchain (e.g., MinGW-w64) and cross-compiled versions of all dependencies (SDL2, FFmpeg, libusb).

### Packaging

#### Linux desktop integration

The `app/data/` directory contains standard Linux desktop integration files:

- `scrcpy.desktop` — standard desktop entry
- `scrcpy-console.desktop` — desktop entry that keeps terminal open
- `icon.png`, `icon.svg`, `icon.ico` — application icons in multiple formats
- `bash-completion/scrcpy` — Bash tab completion
- `zsh-completion/_scrcpy` — Zsh tab completion

#### Windows packaging

- `scrcpy-windows.manifest` — Windows application manifest
- `scrcpy-windows.rc` — Windows resource file (icon, version info)
- `open_a_terminal_here.bat` — helper batch file
- `scrcpy-console.bat` — launch with console window
- `scrcpy-noconsole.vbs` — launch without console window

#### Man page

A man page is provided at `app/scrcpy.1` for Unix-like systems.

### Server deployment on device

The server JAR is pushed to `/data/local/tmp/scrcpy-server.jar` on the device. On the host, the client looks for the server binary at:

1. The path specified by `SCRCPY_SERVER_PATH` environment variable
2. `PREFIX/share/scrcpy/scrcpy-server` (standard install location)

---

## Operations

### Running scrcpy

#### Basic usage

```bash
# Mirror default device over USB
scrcpy

# Mirror specific device
scrcpy -s DEVICE_SERIAL

# Mirror over TCP/IP
scrcpy --tcpip=192.168.1.100:5555

# Record while mirroring
scrcpy --record=session.mp4

# Mirror without audio
scrcpy --no-audio

# Mirror-only, no control
scrcpy --no-control
```

#### Display modes

```bash
# Fullscreen
scrcpy --fullscreen

# Always on top, borderless
scrcpy --always-on-top --window-borderless

# No display window (recording only)
scrcpy --no-window --record=capture.mp4

# Limit resolution
scrcpy --max-size=1024

# Mirror secondary display
scrcpy --display-id=1
```

#### Camera mirroring

```bash
# List available cameras
scrcpy --list-cameras

# Mirror front camera
scrcpy --video-source=camera --camera-facing=front

# Specific camera at specific size
scrcpy --video-source=camera --camera-id=0 --camera-size=1920x1080
```

### Monitoring

#### FPS counter

scrcpy includes a built-in FPS counter (`fps_counter.c`) that can be enabled to log frame rates during a session. Verbosity level `debug` or lower will show frame timing information.

```bash
scrcpy --verbosity=debug
```

#### Log verbosity levels

| Level | Content |
|-------|---------|
| `verbose` | All internal tracing |
| `debug` | Debug information including frame timing |
| `info` | Normal operational messages (default) |
| `warn` | Warnings only |
| `error` | Errors only |

### Updating

scrcpy is a single-version deployment: the client and server must match. When updating:

1. Build or download the new client binary.
2. Ensure the matching server JAR is available at the expected path.
3. The old server on the device is overwritten on each connection (pushed fresh every time).

> [!CAUTION]
> The client and server versions must be identical. A version mismatch will cause connection failures or undefined behavior.

### File transfer

Drag and drop files onto the scrcpy window to push them to the device:

- Regular files are pushed to the directory specified by `--push-target` (default: `/sdcard/Download/`).
- APK files are automatically installed via `adb install`.

This is handled by `file_pusher.c`, which uses `adb push` under the hood.

---

## Performance Tuning

### Video encoding

#### Codec selection

| Codec | Flag | Notes |
|-------|------|-------|
| H.264 | `--video-codec=h264` | Default. Widest hardware support. |
| H.265 (HEVC) | `--video-codec=h265` | Better compression, may not be available on all devices. |
| AV1 | `--video-codec=av1` | Best compression, limited device support. |

#### Bit rate tuning

The default video bit rate is `8M` (8 Mbps). Adjust based on use case:

```bash
# Low bandwidth / low latency
scrcpy --video-bit-rate=2M

# High quality recording
scrcpy --video-bit-rate=16M
```

#### Resolution limiting

```bash
# Limit to 1024px on the longest dimension
scrcpy --max-size=1024

# Cap framerate
scrcpy --max-fps=30
```

> [!TIP]
> Reducing `--max-size` and `--max-fps` significantly decreases encoding load on the device and bandwidth usage, improving latency on slower connections.

### Audio buffering

The audio pipeline uses a regulator (`audio_regulator.c`) that compensates for jitter using libswresample-based sample rate adjustment. When underflow occurs, silence is inserted to prevent audio glitches.

| Parameter | Default | Effect |
|-----------|---------|--------|
| `--audio-buffer` | 50 ms | Jitter buffer before playback. Higher = smoother but more latency. |
| `--audio-output-buffer` | 5 ms | SDL output buffer. Higher = less CPU but more latency. |

```bash
# Minimize audio latency (may cause glitches on poor connections)
scrcpy --audio-buffer=20 --audio-output-buffer=2

# Maximize audio stability
scrcpy --audio-buffer=200 --audio-output-buffer=20
```

### Video buffering

| Parameter | Default | Effect |
|-----------|---------|--------|
| `--video-buffer` | 0 ms | Delay buffer for jitter compensation. 0 = no buffering (lowest latency). |

The delay buffer (`delay_buffer.c`) holds frames to smooth out jitter. The frame buffer (`frame_buffer.c`) is a thread-safe single-frame buffer that always holds the latest decoded frame, dropping older frames when the display cannot keep up.

```bash
# Add 50ms video buffer for unstable connections
scrcpy --video-buffer=50
```

### Codec options

Pass encoder-specific options directly to the device encoder:

```bash
# Set H.264 profile and level
scrcpy --video-codec-options=profile=1,level=2048
```

### Encoder selection

List and select specific hardware/software encoders:

```bash
# List available encoders
scrcpy --list-encoders

# Force specific encoder
scrcpy --video-encoder=OMX.qcom.video.encoder.avc
```

> [!NOTE]
> The packet merger (`packet_merger.c`) reassembles codec packets that may be split across multiple reads. This is transparent to the user but is relevant when debugging encoding issues.

### Screen-off mode performance

Using `--turn-screen-off` / `-S` disables the device display, which can affect encoding performance on some devices (see GitHub issue #3376). If you observe performance degradation, test without this flag.

---

## Security

### Hidden Android API access

The server accesses non-public Android APIs via Java reflection through the `wrappers/` package. This is the mechanism that allows scrcpy to function without root and without installing an APK.

#### Wrapped system services

| Wrapper | Android Service | Purpose |
|---------|----------------|---------|
| `ActivityManager` | `activity` | Task management |
| `ClipboardManager` | `clipboard` | Clipboard sync |
| `ContentProvider` | Content resolver | Content access |
| `DisplayControl` | Display service | Display control |
| `DisplayManager` | `display` | Display enumeration |
| `InputManager` | `input` | Touch/key injection |
| `PowerManager` | `power` | Wake/sleep control |
| `ServiceManager` | `servicemanager` | Service resolution |
| `StatusBarManager` | `statusbar` | Status bar control |
| `SurfaceControl` | Surface compositor | Screen capture |
| `WindowManager` | `window` | Window management |

> [!WARNING]
> These wrappers use reflection to access hidden (`@hide`) Android APIs. These APIs are not part of the public Android SDK and may change or be removed in any Android version. The server targets `compileSdk: 36` but must handle API differences across Android versions (API 21+).

### Network security

#### ADB connection model

scrcpy communicates exclusively over adb. There is no independent network protocol.

- **USB mode**: All traffic flows over the USB cable. No network exposure.
- **TCP/IP mode** (`adb tcpip` / `--tcpip`): ADB listens on port 5555 (default) on the device's network interface. This is an ADB-level concern, not scrcpy-specific.

> [!CAUTION]
> When using ADB over TCP/IP, the ADB connection is **unencrypted and unauthenticated** on the network. Anyone on the same network can connect to the device. Use TCP/IP mode only on trusted networks. Consider using a VPN or SSH tunnel for remote connections.

#### Socket communication

- The client and server communicate via Unix domain sockets tunneled through adb (using `adb reverse` or `adb forward`).
- Socket name prefix: `scrcpy_`
- No TLS or encryption is applied at the scrcpy level; security relies on the adb transport layer.

### Permissions model

scrcpy requires only that USB debugging be enabled on the device. It does not require:

- Root access
- Device unlock (though initial ADB authorization requires user approval on the device)
- Any installed APK

The server runs as the `shell` user, which has elevated privileges compared to normal apps (this is what enables input injection, screen capture, and clipboard access without root).

### Data handling

- **No data persistence on device**: The server JAR at `/data/local/tmp/scrcpy-server.jar` is the only file written to the device. It is overwritten on each connection.
- **Clipboard sync**: When clipboard sync is enabled (default), clipboard content is transmitted between host and device. Use `--no-control` to disable all device control including clipboard sync.
- **File push**: Files dragged onto the window are pushed via `adb push` to the push target directory.

---

## Developer Internals

### Repository structure

```
scrcpy/
├── app/
│   ├── meson.build                    # Client build definition
│   ├── scrcpy.1                       # Man page
│   ├── scrcpy-windows.manifest        # Windows manifest
│   ├── scrcpy-windows.rc              # Windows resource file
│   ├── data/
│   │   ├── icon.ico, icon.png, icon.svg   # App icons
│   │   ├── scrcpy.desktop                 # Linux desktop entry
│   │   ├── scrcpy-console.desktop         # Console desktop entry
│   │   ├── open_a_terminal_here.bat       # Windows helper
│   │   ├── scrcpy-console.bat             # Windows console launcher
│   │   ├── scrcpy-noconsole.vbs           # Windows no-console launcher
│   │   ├── bash-completion/scrcpy         # Bash completions
│   │   └── zsh-completion/_scrcpy         # Zsh completions
│   ├── src/                           # Client C source (67 files)
│   └── tests/                         # Client unit tests (11 files)
├── server/
│   ├── build.gradle                   # Server Gradle build
│   ├── build_without_gradle.sh        # Build without Gradle
│   ├── proguard-rules.pro             # ProGuard config
│   ├── meson.build                    # Server Meson integration
│   ├── scripts/build-wrapper.sh       # Build wrapper
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── aidl/                  # AIDL interfaces
│       │   └── java/com/genymobile/scrcpy/
│       │       ├── Server.java        # Entry point
│       │       ├── Options.java       # Server options
│       │       ├── audio/             # Audio capture & encoding
│       │       ├── control/           # Control message handling
│       │       ├── device/            # Device interaction
│       │       ├── opengl/            # OpenGL support
│       │       ├── util/              # Utilities
│       │       ├── video/             # Video capture & encoding
│       │       └── wrappers/          # Hidden API wrappers
│       └── test/                      # Server unit tests
├── config/
│   ├── android-checkstyle.gradle      # Checkstyle Gradle integration
│   └── checkstyle/checkstyle.xml      # Checkstyle rules
├── doc/                               # 18 topic-specific docs
├── gradle/wrapper/                    # Gradle wrapper
├── meson.build                        # Root Meson build
├── meson_options.txt                  # Client build options
├── build.gradle                       # Root Gradle build
├── cross_win32.txt                    # Win32 cross-compilation
├── cross_win64.txt                    # Win64 cross-compilation
└── install_release.sh                 # Automated install script
```

### Module reference (client)

#### Core orchestration

| Module | Purpose |
|--------|---------|
| `main.c` | Entry point, argument parsing dispatch |
| `cli.c` / `cli.h` | Complete CLI option parsing and validation |
| `scrcpy.c` / `scrcpy.h` | Main orchestration: initializes all subsystems, connects components |
| `options.h` | Options struct definition and defaults |

#### Server management

| Module | Purpose |
|--------|---------|
| `server.c` / `server.h` | Push server JAR, launch via adb shell, establish socket connections |
| `adb/adb.c` / `adb.h` | ADB command execution, device listing, port management |
| `adb/adb_parser.c` | Parse ADB output (device list, etc.) |
| `adb/adb_tunnel.c` | Tunnel setup (reverse/forward) |

#### Video pipeline

| Module | Purpose |
|--------|---------|
| `demuxer.c` / `demuxer.h` | Receive and demultiplex encoded packets from server |
| `decoder.c` / `decoder.h` | FFmpeg-based video/audio decoding |
| `display.c` / `display.h` | SDL texture management and rendering |
| `screen.c` / `screen.h` | SDL window management, resize, rotation |
| `frame_buffer.c` / `frame_buffer.h` | Thread-safe latest-frame buffer (drops old frames) |
| `delay_buffer.c` / `delay_buffer.h` | Jitter compensation buffer |
| `packet_merger.c` / `packet_merger.h` | Reassemble split codec packets |

#### Audio pipeline

| Module | Purpose |
|--------|---------|
| `audio_player.c` / `audio_player.h` | SDL audio callback playback |
| `audio_regulator.c` / `audio_regulator.h` | Real-time audio buffering with libswresample compensation, silence insertion on underflow |

#### Input and control

| Module | Purpose |
|--------|---------|
| `input_manager.c` / `input_manager.h` | Process SDL events, dispatch to appropriate processor |
| `controller.c` / `controller.h` | Serialize and send control messages to server |
| `control_msg.c` / `control_msg.h` | Control message serialization format |
| `device_msg.c` / `device_msg.h` | Device-to-host message deserialization |
| `hid/hid_keyboard.c` | HID keyboard protocol |
| `hid/hid_mouse.c` | HID mouse protocol |
| `hid/hid_gamepad.c` | HID gamepad protocol |
| `uhid/` | Linux UHID kernel module interface |
| `usb/` | AOAv2 USB protocol, libusb integration |
| `usb/scrcpy_otg.c` | OTG mode (direct USB HID, no mirroring) |

#### Recording and output

| Module | Purpose |
|--------|---------|
| `recorder.c` / `recorder.h` | FFmpeg muxing to mp4/mkv/m4a/mka/opus/aac/flac/wav |
| `v4l2_sink.c` / `v4l2_sink.h` | Output to v4l2loopback device (Linux only) |

#### Utilities

| Module | Purpose |
|--------|---------|
| `file_pusher.c` / `file_pusher.h` | Drag-and-drop file/APK transfer via adb push |
| `fps_counter.c` / `fps_counter.h` | Frame rate measurement and logging |
| `util/str.c` / `strbuf.c` | String utilities and string buffer |
| `util/binary.c` | Binary read/write helpers |
| `util/vector.c` / `vecdeque.c` | Dynamic array and deque data structures |

### Trait system (interface pattern)

scrcpy implements a trait-based interface pattern in C using function pointers in structs. This enables polymorphic composition of pipeline stages.

#### Frame traits

| Trait | Header | Methods |
|-------|--------|---------|
| `sc_frame_sink` | `trait/frame_sink.h` | `open()`, `close()`, `push()` -- receives decoded frames |
| `sc_frame_source` | `trait/frame_source.h` | `add_sink()`, `remove_sink()` -- dispatches frames to sinks |

#### Packet traits

| Trait | Header | Methods |
|-------|--------|---------|
| `sc_packet_sink` | `trait/packet_sink.h` | `open()`, `close()`, `push()` -- receives encoded packets |
| `sc_packet_source` | `trait/packet_source.h` | `add_sink()`, `remove_sink()` -- dispatches packets to sinks |

#### Input traits

| Trait | Header | Methods |
|-------|--------|---------|
| `sc_key_processor` | `trait/key_processor.h` | Key event processing interface |
| `sc_mouse_processor` | `trait/mouse_processor.h` | Mouse event processing interface |
| `sc_gamepad_processor` | `trait/gamepad_processor.h` | Gamepad event processing interface |

This system allows components to be composed flexibly. For example, a demuxer (packet source) can feed packets to both a decoder and a recorder simultaneously. A decoder (frame source) can feed frames to both a display and a V4L2 sink.

```
Demuxer (packet_source)
  ├──► Decoder (packet_sink + frame_source)
  │       ├──► Display (frame_sink)
  │       └──► V4L2 Sink (frame_sink)
  └──► Recorder (packet_sink)
```

### Server module reference

| Package | Key Classes | Purpose |
|---------|-------------|---------|
| (root) | `Server`, `Options` | Entry point, option parsing |
| `audio/` | `AudioCapture`, `AudioDirectCapture`, `AudioPlaybackCapture`, `AudioEncoder`, `AudioRawRecorder` | Audio capture and encoding |
| `video/` | `ScreenCapture`, `CameraCapture`, `NewDisplayCapture`, `SurfaceEncoder`, `Streamer` | Video capture and encoding |
| `control/` | `Controller`, `ControlMessageReader` | Input injection |
| `device/` | Device interaction helpers | Device state management |
| `opengl/` | OpenGL utilities | GPU-accelerated processing |
| `util/` | Utility classes | String utils, binary helpers |
| `wrappers/` | 11 wrapper classes | Hidden Android API access via reflection |

#### AIDL interfaces

- `IOnPrimaryClipChangedListener.aidl` -- clipboard change notifications
- `IDisplayWindowListener.aidl` -- display window events

### Code style and quality

- **Server code style**: Enforced via Checkstyle (`config/checkstyle/checkstyle.xml`), integrated through `config/android-checkstyle.gradle`.
- **Client code style**: C99 standard, consistent naming conventions (`sc_` prefix for scrcpy types).
- **ProGuard**: Release server builds use ProGuard rules from `server/proguard-rules.pro`.

### Testing

#### Client tests

Located in `app/tests/`, 11 test files:

| Test | Coverage |
|------|----------|
| `test_adb_parser` | ADB output parsing |
| `test_audiobuf` | Audio buffer operations |
| `test_binary` | Binary read/write utilities |
| `test_cli` | CLI argument parsing |
| `test_control_msg_serialize` | Control message serialization |
| `test_device_msg_deserialize` | Device message deserialization |
| `test_orientation` | Orientation calculations |
| `test_str` | String utilities |
| `test_strbuf` | String buffer operations |
| `test_vecdeque` | Vector deque data structure |
| `test_vector` | Dynamic vector data structure |

Running client tests:

```bash
meson setup build
ninja -C build
meson test -C build
```

#### Server tests

Located in `server/src/test/`:

| Test | Coverage |
|------|----------|
| `ControlMessageReaderTest` | Control message deserialization |
| `DeviceMessageWriterTest` | Device message serialization |
| `BinaryTest` | Binary utilities |
| `CodecOptionsTest` | Codec option parsing |
| `CommandParserTest` | Command parsing |
| `StringUtilsTest` | String utilities |

Running server tests:

```bash
cd server
./gradlew test
```

### Build system details

#### Meson build (client)

The root `meson.build` defines the overall project. `app/meson.build` specifies the client target with all source files, dependencies, and build options.

Key Meson dependencies resolved via pkg-config:
- `sdl2`
- `libavcodec`, `libavformat`, `libavutil`, `libswresample`
- `libusb-1.0` (optional)

Build options are defined in `meson_options.txt`.

#### Gradle build (server)

- Android Gradle Plugin: `8.13.0`
- `compileSdk`: 36
- `minSdk`: 21
- `targetSdk`: 36
- Version: `3.3.4`
- Test dependency: `junit:junit:4.13.2`

> [!TIP]
> The server can be built without Gradle using `server/build_without_gradle.sh`, which invokes the Android SDK tools directly. This is useful in CI environments or when Gradle is not available.

---

## Integration Points

### V4L2 sink (Linux only)

scrcpy can output video to a V4L2 loopback device, making the Android screen appear as a virtual webcam:

```bash
# Load v4l2loopback kernel module
sudo modprobe v4l2loopback

# Stream to V4L2 device
scrcpy --v4l2-sink=/dev/video2

# With buffering
scrcpy --v4l2-sink=/dev/video2 --v4l2-buffer=50
```

The V4L2 sink (`v4l2_sink.c`) implements the `sc_frame_sink` trait, receiving decoded frames from the decoder and writing them to the V4L2 device. It can operate alongside the normal display window, or with `--no-window` for headless streaming.

```bash
# Headless V4L2 streaming (no display window)
scrcpy --v4l2-sink=/dev/video2 --no-window

# V4L2 + recording simultaneously
scrcpy --v4l2-sink=/dev/video2 --record=capture.mp4
```

### OTG mode

OTG mode (`usb/scrcpy_otg.c`) provides direct USB HID input without any mirroring or ADB connection. The host sends keyboard, mouse, and gamepad input directly over USB using the AOAv2 (Android Open Accessory v2) protocol.

```bash
# OTG mode with keyboard and mouse
scrcpy --otg

# OTG mode with specific device
scrcpy --otg -s DEVICE_SERIAL
```

> [!IMPORTANT]
> OTG mode requires libusb and only works on Linux. It bypasses ADB entirely -- the device appears as a USB accessory. No video/audio mirroring occurs in OTG mode.

### HID input modes

scrcpy supports three input injection methods, selectable independently for keyboard, mouse, and gamepad:

| Mode | Method | Requirements | Behavior |
|------|--------|-------------|----------|
| `sdk` | Android InputManager API | ADB connection | Translates to Android key/touch events. Works everywhere but may not handle all key combinations. |
| `uhid` | Linux UHID kernel module | Linux host, ADB | Creates virtual HID devices via `/dev/uhid`. Physical-level input simulation. |
| `aoa` | AOAv2 USB protocol | Linux host, USB, libusb | USB HID over AOAv2. Physical-level input, works even when screen is off. |

```bash
# UHID keyboard + mouse
scrcpy --keyboard=uhid --mouse=uhid

# AOA keyboard + mouse
scrcpy --keyboard=aoa --mouse=aoa

# Mixed: UHID keyboard, SDK mouse
scrcpy --keyboard=uhid --mouse=sdk
```

### Virtual display

scrcpy can create a new virtual display on the device instead of mirroring an existing one:

```bash
# Create a new virtual display
scrcpy --new-display

# Specify virtual display resolution
scrcpy --new-display=1920x1080
```

This uses the `NewDisplayCapture` server component, which creates an Android virtual display and captures its content.

### Recording integration

Recording works independently of display and can capture to multiple container formats:

| Extension | Format | Content |
|-----------|--------|---------|
| `.mp4` | MP4 | Video + Audio |
| `.mkv` | Matroska | Video + Audio |
| `.m4a` | M4A | Audio only |
| `.mka` | Matroska Audio | Audio only |
| `.opus` | Opus | Audio only |
| `.aac` | AAC | Audio only |
| `.flac` | FLAC | Audio only |
| `.wav` | WAV | Audio only |

```bash
# Record video + audio to MP4
scrcpy --record=session.mp4

# Record audio only
scrcpy --no-video --record=audio.opus

# Record without display
scrcpy --no-playback --record=capture.mkv
```

### Known issues (from GitHub)

| Issue | Description |
|-------|-------------|
| #3376 | `--turn-screen-off` (`-S`) may cause performance loss on some devices |
| #4821 | Proprietary apps may show black screen (DRM-protected content) |
| #6742 | 30 FPS cap reported on Samsung MediaTek devices |
| #6682 | Screenshot triggering |
| #6632 | Native window resize behavior |
| #6767 | Custom keyboard layout support |
| #6759 | Aspect ratio during resize |
| #1706 | Using scrcpy without ADB |

---

_Generated May 2026 from Genymobile/scrcpy at 247a37d. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
