This page describes the scrcpy Android server component: its purpose, how it is packaged and launched on the device, the startup sequence, and how its major subsystems are organized. The server is a Java application that runs on the Android device; it is the counterpart to the C client application.
For details on how the client pushes and launches the server via ADB, see ADB and Server Management. For the individual server subsystems, see:
The scrcpy Android server is a self-contained Java JAR (scrcpy-server) that is pushed to the device and executed there for the duration of a mirroring session. Its responsibilities are:
MediaCodecCleanUp)The server has no persistent installation on the device. It is pushed fresh on each invocation and deleted (by default) on exit.
Sources: server/src/main/java/com/genymobile/scrcpy/Server.java1-276
The server is delivered as a DEX-format JAR named scrcpy-server. On the host, the client locates it via the SCRCPY_SERVER_PATH environment variable, or from a compiled-in path (PREFIX/share/scrcpy/scrcpy-server), or the current directory in portable builds.
The client pushes the JAR to /data/local/tmp/scrcpy-server.jar on the device:
adb -s <serial> push scrcpy-server /data/local/tmp/scrcpy-server.jar
It is then executed using app_process, Android's mechanism for running Java code in a minimal runtime context without a full Android app process:
adb -s <serial> shell \
CLASSPATH=/data/local/tmp/scrcpy-server.jar \
app_process \
/ \
com.genymobile.scrcpy.Server \
<VERSION> \
[key=value ...]
The class path is set to the JAR, and com.genymobile.scrcpy.Server is specified as the main class. The process runs as the shell user (UID 2000), which has sufficient privileges to access display and input APIs via the hidden Android framework interfaces wrapped in the wrappers/ package.
The version string is passed as the first positional argument. The server immediately compares it against BuildConfig.VERSION_NAME in Options.parse() and throws if there is a mismatch, preventing protocol incompatibilities.
Sources: app/src/server.c204-464 server/src/main/java/com/genymobile/scrcpy/Options.java292-302
All configuration is passed as key=value positional arguments following the version string. There are no flags or positional-only parameters beyond the version.
Diagram: Parameter flow from client to server
Sources: app/src/server.c251-435 server/src/main/java/com/genymobile/scrcpy/Options.java292-527
The table below lists a representative set of parameters:
| Key | Type | Default | Description |
|---|---|---|---|
scid | hex int | — | Session ID (31-bit) to differentiate multiple instances |
log_level | string | debug | One of verbose, debug, info, warn, error |
video | bool | true | Enable video capture and streaming |
audio | bool | true | Enable audio capture and streaming |
video_codec | string | h264 | h264, h265, or av1 |
audio_codec | string | opus | opus, aac, flac, or raw |
video_source | string | display | display or camera |
audio_source | string | output | output, playback, mic, mic variants, voice variants |
max_size | int | 0 | Cap on width/height in pixels (0 = unlimited) |
video_bit_rate | int | 8000000 | Video encoder target bit rate in bits/s |
audio_bit_rate | int | 128000 | Audio encoder target bit rate in bits/s |
tunnel_forward | bool | false | Connection direction: server connects to client if false |
control | bool | true | Whether to open a control channel |
display_id | int | 0 | Android display ID to capture |
cleanup | bool | true | Restore device state and delete JAR on exit |
downsize_on_error | bool | true | Retry at lower resolution on MediaCodec error |
new_display | string | — | Create a virtual display (e.g. 1920x1080/420) |
camera_id | string | — | Specific camera to use with video_source=camera |
Sources: server/src/main/java/com/genymobile/scrcpy/Options.java312-519 app/src/server.c265-434
Diagram: Server startup sequence
Sources: server/src/main/java/com/genymobile/scrcpy/Server.java70-195 server/src/main/java/com/genymobile/scrcpy/Server.java212-275
prepareMainLooper()Because app_process does not set up an Android main looper automatically, Server.internalMain() calls prepareMainLooper() which uses reflection to install a quit-allowed looper as the main looper server/src/main/java/com/genymobile/scrcpy/Server.java197-210 The looper is later driven by Looper.loop() in scrcpy() and terminated by the Completion helper when all async processors have finished.
If any list_* option is set (encoders, displays, cameras, apps), the server enters a special read-only mode: it prints the requested information to stdout and exits without opening any sockets server/src/main/java/com/genymobile/scrcpy/Server.java245-268
Diagram: Server component structure mapped to classes
Sources: server/src/main/java/com/genymobile/scrcpy/Server.java70-195
AsyncProcessor LifecycleAll three main server tasks — video encoding, audio encoding/recording, and control message handling — implement the AsyncProcessor interface. Each runs on its own background thread.
The Completion inner class counts running processors. When a processor calls its completion callback with fatalError=true, or when the last processor finishes normally, Looper.getMainLooper().quitSafely() is called, which unblocks Looper.loop() in scrcpy(), and cleanup begins.
server/src/main/java/com/genymobile/scrcpy/Server.java47-65
Sources: server/src/main/java/com/genymobile/scrcpy/Server.java161-194
The server opens between one and three TCP sockets depending on which streams are enabled. The direction of connection depends on whether tunnel_forward is set.
| Socket | Condition | Data direction |
|---|---|---|
| Video socket | video=true | Server → Client (encoded video frames) |
| Audio socket | audio=true | Server → Client (encoded audio frames) |
| Control socket | control=true | Client → Server (commands); Server → Client (device messages) |
adb reverse mode (default): The client listens on a local port; the server connects to it through an adb reverse tunnel. tunnel_forward=false.
adb forward mode (--force-adb-forward): The client creates an adb forward tunnel; the server listens and the client connects. tunnel_forward=true.
The first socket opened also carries the initial handshake: the server writes a single dummy byte to confirm the connection is alive, then sends the 64-byte device name field if send_device_meta=true. The client reads both before starting the demuxers.
Sources: server/src/main/java/com/genymobile/scrcpy/Server.java93-104 app/src/server.c466-742
The scrcpy() method in Server.java selects concrete implementations based on the parsed options:
video_source=display AND new_display != null → NewDisplayCapture
video_source=display AND new_display == null → ScreenCapture
video_source=camera → CameraCapture
audio_source.isDirect() → AudioDirectCapture
otherwise → AudioPlaybackCapture
audio_codec=raw → AudioRawRecorder (no MediaCodec)
otherwise → AudioEncoder (uses MediaCodec)
Sources: server/src/main/java/com/genymobile/scrcpy/Server.java118-158
The server source lives under server/src/main/java/com/genymobile/scrcpy/. The key packages are:
| Package / File | Role |
|---|---|
Server.java | Entry point, startup orchestration |
Options.java | Parameter parsing from key=value args |
CleanUp.java | Restores device settings on exit |
Workarounds.java | Bootstraps ActivityThread/Application context |
device/DesktopConnection.java | Manages the socket connections to the client |
device/Device.java | Input injection, clipboard, display power |
video/SurfaceEncoder.java | MediaCodec-based video encode loop |
video/ScreenCapture.java | Display capture via SurfaceControl |
video/CameraCapture.java | Camera capture via Camera2 API |
video/NewDisplayCapture.java | Virtual display creation and capture |
audio/AudioEncoder.java | MediaCodec-based audio encode loop |
audio/AudioRawRecorder.java | Raw PCM audio passthrough |
audio/AudioDirectCapture.java | AudioRecord-based capture |
audio/AudioPlaybackCapture.java | Playback/output audio capture |
control/Controller.java | Control message dispatch loop |
control/ControlMessageReader.java | Binary deserialization of control messages |
wrappers/ | Reflection-based wrappers for hidden Android APIs |
Sources: server/src/main/java/com/genymobile/scrcpy/Server.java1-36
Refresh this wiki
This wiki was recently refreshed. Please wait 3 days to refresh again.