scrcpy Documentation

Display and control your Android device

145,660 stars C C 1 file Β· ~17,081 tokens #android#c#ffmpeg#libav#mirroring#recording#screen#sdl2
RAW Doc

File: README.md

[!WARNING]
This GitHub repo (https://github.com/Genymobile/scrcpy) is the only official
source for the project. Do not download releases from random websites, even if
their name contains scrcpy.

scrcpy (v4.1)

pronounced "screen copy"

This application mirrors Android devices (video and audio) connected via USB or
TCP/IP and allows control using the
computer's keyboard and mouse. It does not require root access or an app
installed on the device. It works on Linux, Windows, and macOS.

 
 
 

It focuses on:

  • lightness: native, displays only the device screen
  • performance: 30~120fps, depending on the device
  • quality: 1920Γ—1080 or above
  • low latency: 35~70ms
  • low startup time: ~1 second to display the first image
  • non-intrusiveness: nothing is left installed on the Android device
  • user benefits: no account, no ads, no internet required
  • freedom: free and open source software

Its features include:

Prerequisites

The Android device requires at least API 21 (Android 5.0).

Audio forwarding is supported for API >= 30 (Android 11+).

Make sure you enabled USB debugging on your device(s).

On some devices (especially Xiaomi), you might get the following error:

text
Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission.

In that case, you need to enable an additional option USB debugging (Security Settings) (this is an item different from USB debugging) to control
it using a keyboard and mouse. Rebooting the device is necessary once this
option is set.

Note that USB debugging is not required to run scrcpy in OTG mode.

Get the app

Must-know tips

Usage examples

There are a lot of options, documented in separate pages.
Here are just some common examples.

  • Capture the screen in H.265 (better quality), limit the size to 1920, limit
    the frame rate to 60fps, disable audio, and control the device by simulating
    a physical keyboard:

    bash
    scrcpy --video-codec=h265 --max-size=1920 --max-fps=60 --no-audio --keyboard=uhid
    scrcpy --video-codec=h265 -m1920 --max-fps=60 --no-audio -K  # short version
  • Start VLC in a new virtual display (separate from the device display):

    bash
    scrcpy --new-display=1920x1080 --start-app=org.videolan.vlc
  • Start VLC in a new flex display using H.265 with a bitrate of 16 Mbps,
    while keeping the display active so it does not turn off:

    bash
    scrcpy --new-display -x --keep-active --start-app=org.videolan.vlc --video-codec=h265 -b16M
  • Record the device camera in H.265 at 1920x1080 (and microphone) to an MP4
    file:

    bash
    scrcpy --video-source=camera --video-codec=h265 --camera-size=1920x1080 --record=file.mp4
  • Capture the device front camera and expose it as a webcam on the computer (on
    Linux):

    bash
    scrcpy --video-source=camera --camera-size=1920x1080 --camera-facing=front --v4l2-sink=/dev/video2 --no-playback
  • Control the device without mirroring by simulating a physical keyboard and
    mouse (USB debugging not required):

    bash
    scrcpy --otg
  • Control the device using gamepads plugged into the computer:

    bash
    scrcpy --gamepad=uhid
    scrcpy -G  # short version

User documentation

The application provides a lot of features and configuration options. They are
documented in the following pages:

Resources

Articles

Contact

You can open an issue for bug reports, feature requests or general questions.

For bug reports, please read the FAQ first, you might find a solution
to your problem immediately.

You can also use:

I'm @rom1v, the author and maintainer of scrcpy.

If you appreciate this application, you can support my open source
work
:

License

text
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2026 Romain Vimont

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

File: doc/audio.md

Audio

Audio forwarding is supported for devices with Android 11 or higher, and it is
enabled by default:

  • For Android 12 or newer, it works out-of-the-box.
  • For Android 11, you'll need to ensure that the device screen is unlocked
    when starting scrcpy. A fake popup will briefly appear to make the system
    think that the shell app is in the foreground. Without this, audio capture
    will fail.
  • For Android 10 or earlier, audio cannot be captured and is automatically
    disabled.

If audio capture fails, then mirroring continues with video only (since audio is
enabled by default, it is not acceptable to make scrcpy fail if it is not
available), unless --require-audio is set.

No audio

To disable audio:

bash
scrcpy --no-audio

To disable only the audio playback, see no playback.

Audio only

To play audio only, disable video and control:

bash
scrcpy --no-video --no-control

To play audio without a window:

bash
# --no-video and --no-control are implied by --no-window
scrcpy --no-window
# interrupt with Ctrl+C

Without video, the audio latency is typically not critical, so it might be
interesting to add buffering to minimize glitches:

bash
scrcpy --no-video --audio-buffer=200

Source

By default, the device audio output is forwarded.

It is possible to capture the device microphone instead:

bash
scrcpy --audio-source=mic

For example, to use the device as a dictaphone and record a capture directly on
the computer:

bash
scrcpy --audio-source=mic --no-video --no-playback --record=file.opus

Many sources are available:

  • output (default): forwards the whole audio output, and disables playback on the device (mapped to REMOTE_SUBMIX).
  • playback: captures the audio playback (Android apps can opt-out, so the whole output is not necessarily captured).
  • mic: captures the microphone (mapped to MIC).
  • mic-unprocessed: captures the microphone unprocessed (raw) sound (mapped to UNPROCESSED).
  • mic-camcorder: captures the microphone tuned for video recording, with the same orientation as the camera if available (mapped to CAMCORDER).
  • mic-voice-recognition: captures the microphone tuned for voice recognition (mapped to VOICE_RECOGNITION).
  • mic-voice-communication: captures the microphone tuned for voice communications (it will for instance take advantage of echo cancellation or automatic gain control if available) (mapped to VOICE_COMMUNICATION).
  • voice-call: captures voice call (mapped to VOICE_CALL).
  • voice-call-uplink: captures voice call uplink only (mapped to VOICE_UPLINK).
  • voice-call-downlink: captures voice call downlink only (mapped to VOICE_DOWNLINK).
  • voice-performance: captures audio meant to be processed for live performance (karaoke), includes both the microphone and the device playback (mapped to VOICE_PERFORMANCE).

Duplication

An alternative device audio capture method is also available (only for Android
13 and above):

bash
scrcpy --audio-source=playback

This audio source supports keeping the audio playing on the device while
mirroring, with --audio-dup:

bash
scrcpy --audio-source=playback --audio-dup
# or simply:
scrcpy --audio-dup  # --audio-source=playback is implied

However, it requires Android 13, and Android apps can opt-out (so they are not
captured).

See #4380.

Codec

The audio codec can be selected. The possible values are opus (default),
aac, flac and raw (uncompressed PCM 16-bit LE):

bash
scrcpy --audio-codec=opus  # default
scrcpy --audio-codec=aac
scrcpy --audio-codec=flac
scrcpy --audio-codec=raw

In particular, if you get the following error:

Failed to initialize audio/opus, error 0xfffffffe

then your device has no Opus encoder: try scrcpy --audio-codec=aac.

For advanced usage, to pass arbitrary parameters to the MediaFormat,
check --audio-codec-options in the manpage or in scrcpy --help.

For example, to change the FLAC compression level:

bash
scrcpy --audio-codec=flac --audio-codec-options=flac-compression-level=8

Encoder

Several encoders may be available on the device. They can be listed by:

bash
scrcpy --list-encoders

To select a specific encoder:

bash
scrcpy --audio-codec=opus --audio-encoder='c2.android.opus.encoder'

Bit rate

The default audio bit rate is 128Kbps. To change it:

bash
scrcpy --audio-bit-rate=64K
scrcpy --audio-bit-rate=64000  # equivalent

This parameter does not apply to RAW audio codec (--audio-codec=raw).

Buffering

Audio buffering is unavoidable. It must be kept small enough so that the latency
is acceptable, but large enough to minimize buffer underrun (causing audio
glitches).

The default buffer size is set to 50ms. It can be adjusted:

bash
scrcpy --audio-buffer=40   # smaller than default
scrcpy --audio-buffer=100  # higher than default

Note that this option changes the target buffering. It is possible that this
target buffering might not be reached (on frequent buffer underflow typically).

If you don't interact with the device (to watch a video for example), a higher
latency (for both video and audio) might be preferable to
avoid glitches and smooth the playback:

bash
scrcpy --video-buffer=200 --audio-buffer=200

It is also possible to configure another audio buffer (the audio output buffer),
by default set to 10ms. Do not change this setting unless you have a good reason
(see robotic and glitchy sound). For instance:

bash
scrcpy --audio-output-buffer=5

File: doc/build.md

Build scrcpy

Here are the instructions to build scrcpy (client and server).

If you just want to build and install the latest release, follow the simplified
process described in doc/linux.md.

Branches

There are two main branches:

  • master: contains the latest release. It is the home page of the project on
    GitHub.
  • dev: the current development branch. Every commit present in dev will be
    in the next release.

If you want to contribute code, please base your commits on the latest dev
branch.

Requirements

You need adb. It is available in the Android SDK platform
tools
, or packaged in your distribution (adb).

On Windows, download the platform-tools and extract
the following files to a directory accessible from your PATH:

  • adb.exe
  • AdbWinApi.dll
  • AdbWinUsbApi.dll

It is also available in scrcpy releases.

The client requires FFmpeg and SDL. Just follow the instructions.

System-specific steps

Linux

Install the required packages from your package manager.

Debian/Ubuntu

bash
# runtime dependencies
sudo apt install ffmpeg libsdl3-0 adb libusb-1.0-0

# client build dependencies
sudo apt install gcc git pkg-config meson ninja-build libsdl3-dev \
                 libavcodec-dev libavdevice-dev libavformat-dev libavutil-dev \
                 libswresample-dev libusb-1.0-0-dev libv4l-dev

# server build dependencies
sudo apt install openjdk-17-jdk

On old versions (like Ubuntu 16.04), meson is too old. In that case, install
it from pip3:

bash
sudo apt install python3-pip
pip3 install meson

Fedora

bash
# enable RPM fusion free
sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm

# client build dependencies
sudo dnf install SDL3-devel ffms2-devel libusb1-devel libavdevice-free-devel meson gcc make

# server build dependencies
sudo dnf install java-devel

Windows

Cross-compile from Linux

This is the preferred method (and the way the release is built).

From Debian, install mingw:

bash
sudo apt install mingw-w64 mingw-w64-tools libz-mingw-w64-dev

You also need the JDK to build the server:

bash
sudo apt install openjdk-17-jdk

Then generate the releases:

bash
./release.sh

It will generate win32 and win64 releases into dist/.

In MSYS2

From Windows, you need MSYS2 to build the project. From an MSYS2 terminal,
install the required packages:

bash
# runtime dependencies
pacman -S mingw-w64-x86_64-sdl3 \
          mingw-w64-x86_64-ffmpeg \
          mingw-w64-x86_64-libusb

# client build dependencies
pacman -S mingw-w64-x86_64-make \
          mingw-w64-x86_64-gcc \
          mingw-w64-x86_64-pkg-config \
          mingw-w64-x86_64-meson

For a 32 bits version, replace x86_64 by i686:

bash
# runtime dependencies
pacman -S mingw-w64-i686-sdl3 \
          mingw-w64-i686-ffmpeg \
          mingw-w64-i686-libusb

# client build dependencies
pacman -S mingw-w64-i686-make \
          mingw-w64-i686-gcc \
          mingw-w64-i686-pkg-config \
          mingw-w64-i686-meson

Java (>= 7) is not available in MSYS2, so if you plan to build the server,
install it manually and make it available from the PATH:

bash
export PATH="$JAVA_HOME/bin:$PATH"

When following the rest of the build instructions below, make sure you use the
MinGW terminal within MSYS2.

macOS

Install the packages with Homebrew:

bash
# runtime dependencies
brew install sdl3 ffmpeg libusb

# client build dependencies
brew install pkg-config meson

Additionally, if you want to build the server, install Java 17 from Caskroom, and
make it available from the PATH:

bash
brew install openjdk@17
export JAVA_HOME="$(/usr/libexec/java_home --version 1.17)"
export PATH="$JAVA_HOME/bin:$PATH"

Docker

See pierlon/scrcpy-docker.

Common steps

As a non-root user, clone the project:

bash
git clone https://github.com/Genymobile/scrcpy
cd scrcpy

Build

You may want to build only the client: the server binary, which will be pushed
to the Android device, does not depend on your system and architecture. In that
case, use the prebuilt server (so you will not need Java or the Android SDK).

Option 1: Build everything from sources

Install the Android SDK (Android Studio), and set ANDROID_SDK_ROOT to its
directory. For example:

bash
# Linux
export ANDROID_SDK_ROOT=~/Android/Sdk
# Mac
export ANDROID_SDK_ROOT=~/Library/Android/sdk
# Windows
set ANDROID_SDK_ROOT=%LOCALAPPDATA%\Android\sdk

Then, build:

bash
meson setup x --buildtype=release --strip -Db_lto=true
ninja -Cx  # DO NOT RUN AS ROOT

Note: ninja must be run as a non-root user (only ninja install must be run as root).

Option 2: Use prebuilt server

  • scrcpy-server-v4.1
    SHA-256: deacb991ed2509715160ffdc7907e47b4160eb30d1566217e9047fd5b8850cae

Download the prebuilt server somewhere, and specify its path during the Meson
configuration:

bash
meson setup x --buildtype=release --strip -Db_lto=true \
    -Dprebuilt_server=/path/to/scrcpy-server
ninja -Cx  # DO NOT RUN AS ROOT

The server only works with a matching client version (this server works with the
master branch).

Run without installing:

bash
./run x [options]

Install

After a successful build, you can install scrcpy on the system:

bash
sudo ninja -Cx install    # without sudo on Windows

This installs several files:

  • /usr/local/bin/scrcpy (main app)
  • /usr/local/share/scrcpy/scrcpy-server (server to push to the device)
  • /usr/local/share/man/man1/scrcpy.1 (manpage)
  • /usr/local/share/icons/hicolor/256x256/apps/scrcpy.png (app icon)
  • /usr/local/share/icons/hicolor/256x256/apps/disconnected.png (device
    disconnected icon)
  • /usr/local/share/zsh/site-functions/_scrcpy (zsh completion)
  • /usr/local/share/bash-completion/completions/scrcpy (bash completion)
  • /usr/local/share/applications/scrcpy.desktop (app-without-console desktop
    entry file)
  • /usr/local/share/applications/scrcpy-console.desktop (app-with-console
    desktop entry file)

You can then run scrcpy.

Uninstall

bash
sudo ninja -Cx uninstall  # without sudo on Windows

File: doc/camera.md

Camera

Camera mirroring is supported for devices with Android 12 or higher.

To capture the camera instead of the device screen:

bash
scrcpy --video-source=camera

By default, it automatically switches audio source to
microphone (as if --audio-source=mic were also passed).

bash
scrcpy --video-source=display  # default is --audio-source=output
scrcpy --video-source=camera   # default is --audio-source=mic
scrcpy --video-source=display --audio-source=mic    # force display AND microphone
scrcpy --video-source=camera --audio-source=output  # force camera AND device audio output

Audio can be disabled:

bash
# audio not captured at all
scrcpy --video-source=camera --no-audio
scrcpy --video-source=camera --no-audio --record=file.mp4

# audio captured and recorded, but not played
scrcpy --video-source=camera --no-audio-playback --record=file.mp4

List

To list the cameras available (with their declared valid sizes and frame rates):

bash
scrcpy --list-cameras
scrcpy --list-camera-sizes

Note that the sizes and frame rates are declarative. They are not accurate on
all devices: some of them are declared but not supported, while some others are
not declared but supported.

Selection

It is possible to pass an explicit camera id (as listed by --list-cameras):

bash
scrcpy --video-source=camera --camera-id=0

Alternatively, the camera may be selected automatically:

bash
scrcpy --video-source=camera                           # use the first camera
scrcpy --video-source=camera --camera-facing=front     # use the first front camera
scrcpy --video-source=camera --camera-facing=back      # use the first back camera
scrcpy --video-source=camera --camera-facing=external  # use the first external camera

If --camera-id is specified, then --camera-facing is forbidden (the id
already determines the camera):

bash
scrcpy --video-source=camera --camera-id=0 --camera-facing=front  # error

Size selection

It is possible to pass an explicit camera size:

bash
scrcpy --video-source=camera --camera-size=1920x1080

The given size may be listed among the declared valid sizes
(--list-camera-sizes), but may also be anything else (some devices support
arbitrary sizes):

bash
scrcpy --video-source=camera --camera-size=1840x444

Alternatively, a declared valid size (among the ones listed by
list-camera-sizes) may be selected automatically.

Two constraints are supported:

  • -m/--max-size (already used for display mirroring), for example -m1920;
  • --camera-ar to specify an aspect ratio (<num>:<den>, <value> or
    sensor).

Some examples:

bash
scrcpy --video-source=camera                          # use the greatest width and the greatest associated height
scrcpy --video-source=camera -m1920                   # use the greatest width not above 1920 and the greatest associated height
scrcpy --video-source=camera --camera-ar=4:3          # use the greatest size with an aspect ratio of 4:3 (+/- 10%)
scrcpy --video-source=camera --camera-ar=1.6          # use the greatest size with an aspect ratio of 1.6 (+/- 10%)
scrcpy --video-source=camera --camera-ar=sensor       # use the greatest size with the aspect ratio of the camera sensor (+/- 10%)
scrcpy --video-source=camera -m1920 --camera-ar=16:9  # use the greatest width not above 1920 and the closest to 16:9 aspect ratio

If --camera-size is specified, then -m/--max-size and --camera-ar are
forbidden (the size is determined by the value given explicitly):

bash
scrcpy --video-source=camera --camera-size=1920x1080 -m3000  # error

Rotation

To rotate the captured video, use the video orientation
option:

bash
scrcpy --video-source=camera --camera-size=1920x1080 --orientation=90

Frame rate

By default, camera is captured at Android's default frame rate (30 fps).

To configure a different frame rate:

bash
scrcpy --video-source=camera --camera-fps=60

High speed capture

The Android camera API also supports a high speed capture mode.

This mode is restricted to specific resolutions and frame rates, listed by
--list-camera-sizes.

bash
scrcpy --video-source=camera --camera-size=1920x1080 --camera-high-speed --camera-fps=240

Brace expansion tip

All camera options start with --camera-, so if your shell supports it, you can
benefit from brace expansion (for example, it is supported by bash and
zsh):

bash
scrcpy --video-source=camera --camera-{facing=back,ar=16:9,high-speed,fps=120}

This will be expanded as:

bash
scrcpy --video-source=camera --camera-facing=back --camera-ar=16:9 --camera-high-speed --camera-fps=120

Torch

The camera torch can be turned on at startup by --camera-torch:

text
scrcpy --video-source=camera --camera-torch

It can also be turned on and off dynamically with MOD+t
and MOD+Shift+t, respectively.

Zoom

The camera zoom can be set with --camera-zoom:

bash
scrcpy --video-source=camera --camera-zoom=1.5

It can also be adjusted dynamically using MOD+↑ (up) and
MOD+↓ (down).

Webcam

Combined with the V4L2 feature on Linux, the Android device camera
may be used as a webcam on the computer.


File: doc/connection.md

Connection

Selection

If exactly one device is connected (i.e. listed by adb devices), then it is
automatically selected.

However, if there are multiple devices connected, you must specify the one to
use in one of 4 ways:

  • by its serial:
    bash
    scrcpy --serial=0123456789abcdef
    scrcpy -s 0123456789abcdef   # short version
    
    # the serial is the ip:port if connected over TCP/IP (same behavior as adb)
    scrcpy --serial=192.168.1.1:5555
  • the one connected over USB (if there is exactly one):
    bash
    scrcpy --select-usb
    scrcpy -d   # short version
  • the one connected over TCP/IP (if there is exactly one):
    bash
    scrcpy --select-tcpip
    scrcpy -e   # short version
  • a device already listening on TCP/IP (see below):
    bash
    scrcpy --tcpip=192.168.1.1:5555
    scrcpy --tcpip=192.168.1.1        # default port is 5555

The serial may also be provided via the environment variable ANDROID_SERIAL
(also used by adb):

bash
# in bash
export ANDROID_SERIAL=0123456789abcdef
scrcpy
cmd
:: in cmd
set ANDROID_SERIAL=0123456789abcdef
scrcpy
powershell
# in PowerShell
$env:ANDROID_SERIAL = '0123456789abcdef'
scrcpy

TCP/IP (wireless)

Scrcpy uses adb to communicate with the device, and adb can connect to a
device over TCP/IP. The device must be connected on the same network as the
computer.

Automatic

An option --tcpip allows to configure the connection automatically. There are
two variants.

If adb TCP/IP mode is disabled on the device (or if you don't know the IP
address), connect the device over USB, then run:

bash
scrcpy --tcpip   # without arguments

It will automatically find the device IP address and adb port, enable TCP/IP
mode if necessary, then connect to the device before starting.

If the device (accessible at 192.168.1.1 in this example) already listens on a
port (typically 5555) for incoming adb connections, then run:

bash
scrcpy --tcpip=192.168.1.1       # default port is 5555
scrcpy --tcpip=192.168.1.1:5555

Prefix the address with a '+' to force a reconnection:

bash
scrcpy --tcpip=+192.168.1.1

Manual

Alternatively, it is possible to enable the TCP/IP connection manually using
adb:

  1. Plug the device into a USB port on your computer.

  2. Connect the device to the same Wi-Fi network as your computer.

  3. Get your device IP address, in Settings β†’ About phone β†’ Status, or by
    executing this command:

    bash
    adb shell ip route | awk '{print $9}'
  4. Enable adb over TCP/IP on your device: adb tcpip 5555.

  5. Unplug your device.

  6. Connect to your device: adb connect DEVICE_IP:5555 (replace DEVICE_IP
    with the device IP address you found)
    .

  7. Run scrcpy as usual.

  8. Run adb disconnect once you're done.

Since Android 11, a wireless debugging option allows you to
bypass having to physically connect your device to your computer.

Autostart

A small tool (by the scrcpy author) allows you to run arbitrary commands
whenever a new Android device is connected: AutoAdb. It can be used to start
scrcpy:

bash
autoadb scrcpy -s '{}'

File: doc/control.md

Control

Read-only

To disable controls (everything which can interact with the device: input keys,
mouse events, drag&drop files):

bash
scrcpy --no-control
scrcpy -n   # short version

Keyboard and mouse

Read keyboard and mouse.

Control only

To control the device without mirroring:

bash
scrcpy --no-video --no-audio

By default, the mouse is disabled when video playback is turned off.

To control the device using a relative mouse, enable UHID mouse mode:

bash
scrcpy --no-video --no-audio --mouse=uhid
scrcpy --no-video --no-audio -M  # short version

To also use a UHID keyboard, set it explicitly:

bash
scrcpy --no-video --no-audio --mouse=uhid --keyboard=uhid
scrcpy --no-video --no-audio -MK  # short version

To use AOA instead (over USB only):

bash
scrcpy --no-video --no-audio --keyboard=aoa --mouse=aoa

Copy-paste

Any time the Android clipboard changes, it is automatically synchronized to the
computer clipboard.

Any Ctrl shortcut is forwarded to the device. In particular:

  • Ctrl+c typically copies
  • Ctrl+x typically cuts
  • Ctrl+v typically pastes (after computer-to-device
    clipboard synchronization)

This typically works as you expect.

The actual behavior depends on the active application though. For example,
Termux sends SIGINT on Ctrl+c instead, and K-9 Mail
composes a new message.

To copy, cut and paste in such cases (but only supported on Android >= 7):

  • MOD+c injects COPY
  • MOD+x injects CUT
  • MOD+v injects PASTE (after computer-to-device
    clipboard synchronization)

In addition, MOD+Shift+v injects the computer
clipboard text as a sequence of key events. This is useful when the component
does not accept text pasting (for example in Termux), but it can break
non-ASCII content.

WARNING: Pasting the computer clipboard to the device (either via
Ctrl+v or MOD+v) copies the content
into the Android clipboard. As a consequence, any Android application could read
its content. You should avoid pasting sensitive content (like passwords) that
way.

Some Android devices do not behave as expected when setting the device clipboard
programmatically. An option --legacy-paste is provided to change the behavior
of Ctrl+v and MOD+v so that they
also inject the computer clipboard text as a sequence of key events (the same
way as MOD+Shift+v).

To disable automatic clipboard synchronization, use
--no-clipboard-autosync.

Pinch-to-zoom, rotate and tilt simulation

To simulate "pinch-to-zoom": Ctrl+click-and-move.

More precisely, hold down Ctrl while pressing the left-click button.
Until the left-click button is released, all mouse movements scale and rotate
the content (if supported by the app) relative to the center of the screen.

https://github.com/Genymobile/scrcpy/assets/543275/26c4a920-9805-43f1-8d4c-608752d04767

To simulate a vertical tilt gesture: Shift+click-and-move-up-or-down.

https://github.com/Genymobile/scrcpy/assets/543275/1e252341-4a90-4b29-9d11-9153b324669f

Similarly, to simulate a horizontal tilt gesture:
Ctrl+Shift+click-and-move-left-or-right.

Technically, scrcpy generates additional touch events from a "virtual finger"
at a location inverted through the center of the screen. When pressing
Ctrl the x and y coordinates are inverted. Using Shift
only inverts x, whereas using Ctrl+Shift only inverts
y.

This only works for the default mouse mode (--mouse=sdk).

File drop

Install APK

To install an APK, drag & drop an APK file (ending with .apk) to the scrcpy
window.

There is no visual feedback, a log is printed to the console.

Push file to device

To push a file to /sdcard/Download/ on the device, drag & drop a (non-APK)
file to the scrcpy window.

There is no visual feedback, a log is printed to the console.

The target directory can be changed on start:

bash
scrcpy --push-target=/sdcard/Movies/

After each successful push, scrcpy requests the media scanner to scan the
"push directory" so that the new files immediately appear in media apps.

Note that some gallery apps only show files from a fixed list of folders
(typically DCIM/Camera) in their main view; the file is then still reachable
through the system Photo Picker and the folder view, but may not appear on the
gallery home screen.


File: doc/develop.md

scrcpy for developers

Overview

This application is composed of two parts:

  • the server (scrcpy-server), to be executed on the device,
  • the client (the scrcpy binary), executed on the host computer.

The client is responsible to push the server to the device and start its
execution.

The client and the server establish communication using separate sockets for
video, audio and controls. Any of them may be disabled (but not all), so
there are 1, 2 or 3 socket(s).

The server initially sends the device name on the first socket (it is used for
the scrcpy window title), then each socket is used for its own purpose. All
reads and writes are performed from a dedicated thread for each socket, both on
the client and on the server.

If video is enabled, then the server sends a raw video stream (H.264 by default)
of the device screen, with some additional headers for each packet. The client
decodes the video frames, and displays them as soon as possible, without
buffering (unless --video-buffer=delay is specified) to minimize latency. The
client is not aware of the device rotation (which is handled by the server), it
just knows the dimensions of the video frames it receives.

Similarly, if audio is enabled, then the server sends a raw audio stream (OPUS
by default) of the device audio output (or the microphone if
--audio-source=mic is specified), with some additional headers for each
packet. The client decodes the stream, attempts to keep a minimal latency by
maintaining an average buffering. The blog post of the scrcpy 2.0
release gives more details about the audio feature.

If control is enabled, then the client captures relevant keyboard and mouse
events, that it transmits to the server, which injects them to the device. This
is the only socket which is used in both direction: input events are sent from
the client to the device, and when the device clipboard changes, the new content
is sent from the device to the client to support seamless copy-paste.

Note that the client-server roles are expressed at the application level:

  • the server serves video and audio streams, and handle requests from the
    client,
  • the client controls the device through the server.

However, by default (when --force-adb-forward is not set), the roles are
reversed at the network level:

  • the client opens a server socket and listen on a port before starting the
    server,
  • the server connects to the client.

This role inversion guarantees that the connection will not fail due to race
conditions without polling.

Server

Privileges

Capturing the screen requires some privileges, which are granted to shell.

The server is a Java application (with a public static void main(String... args) method), compiled against the Android framework, and executed as
shell on the Android device.

To run such a Java application, the classes must be dexed (typically,
to classes.dex). If my.package.MainClass is the main class, compiled to
classes.dex, pushed to the device in /data/local/tmp, then it can be run
with:

text
adb shell CLASSPATH=/data/local/tmp/classes.dex app_process / my.package.MainClass

The path /data/local/tmp is a good candidate to push the server, since it's
readable and writable by shell, but not world-writable, so a malicious
application may not replace the server just before the client executes it.

Instead of a raw dex file, app_process accepts a jar containing
classes.dex (e.g. an APK). For simplicity, and to benefit from the gradle
build system, the server is built to an (unsigned) APK (renamed to
scrcpy-server.jar).

Hidden methods

Although compiled against the Android framework, hidden methods and classes are
not directly accessible (and they may differ from one Android version to
another).

They can be called using reflection though. The communication with hidden
components is provided by wrappers classes and aidl.

Execution

The server is started by the client basically by executing the following
commands:

bash
adb push scrcpy-server /data/local/tmp/scrcpy-server.jar
adb forward tcp:27183 localabstract:scrcpy
adb shell CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 4.0

The first argument (4.0 in the example) is the client scrcpy version. The
server fails if the client and the server do not have the exact same version.
The protocol between the client and the server may change from version to
version (see protocol below), and there is no backward or forward
compatibility (there is no point to use different client and server versions).
This check allows to detect misconfiguration (running an older or newer server
by mistake).

It is followed by any number of arguments, in the form of key=value pairs.
Their order is irrelevant. The possible keys and associated value types can be
found in the server and client code.

For example, if we execute scrcpy -m1920 --no-audio, then the server
execution will look like this:

bash
# scid is a random number to identify different clients running on the same device
adb shell CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 4.0 scid=12345678 log_level=info audio=false max_size=1920

Components

When executed, its main() method is executed (on the "main" thread).
It parses the arguments, establishes the connection with the client and starts
the other "components":

  • the video streamer: it captures the video screen and send encoded video
    packets on the video socket (from the video thread).
  • the audio streamer: it uses several threads to capture raw packets,
    submits them to encoding and retrieve encoded packets, which it sends on the
    audio socket.
  • the controller: it receives control messages (typically input events)
    on the control socket from one thread, and sends device messages (e.g. to
    transmit the device clipboard content to the client) on the same control
    socket
    from another thread. Thus, the control socket is used in both
    directions (contrary to the video and audio sockets).

Screen video encoding

The screen capture is managed by ScreenCapture and the encoding is performed
by SurfaceEncoder.

The video is encoded using the MediaCodec API. The codec encodes the content
of a Surface associated to the display, and writes the encoding packets to the
client (on the video socket).

On device rotation (or folding), the encoding session is reset and restarted.

New frames are produced only when changes occur on the surface. This avoids to
send unnecessary frames, but by default there might be drawbacks:

  • it does not send any frame on start if the device screen does not change,
  • after fast motion changes, the last frame may have poor quality.

Both problems are solved by the flag
KEY_REPEAT_PREVIOUS_FRAME_AFTER.

Audio encoding

Similarly, the audio is captured using an AudioRecord, and encoded using
the MediaCodec asynchronous API.

More details are available on the blog post introducing the audio feature.

Input events injection

Control messages are received from the client by the Controller (run in a
separate thread). There are several types of input events:

  • keycode (cf KeyEvent),
  • text (special characters may not be handled by keycodes directly),
  • mouse motion/click,
  • mouse scroll,
  • other commands (e.g. to switch the screen on or to copy the clipboard).

Some of them need to inject input events to the system. To do so, they use the
hidden method InputManager.injectInputEvent().

Client

The client relies on SDL, which provides cross-platform API for UI, input
events, threading, etc.

The video and audio streams are decoded by FFmpeg.

Initialization

The client parses the command line arguments, then runs one of two code
paths
:

In the remaining of this document, we assume that the "normal" mode is used
(read the code for the OTG mode).

On startup, the client:

  • opens the video, audio and control sockets;
  • pushes and starts the server on the device;
  • initializes its components (demuxers, decoders, recorder…).

Video and audio streams

Depending on the arguments passed to scrcpy, several components may be used.
Here is an overview of the video and audio components:

text
V4L2 sink
                                               /
                                       decoder
                                     /         \
        VIDEO -------------> demuxer             display
                                     \
                                       recorder
                                     /
        AUDIO -------------> demuxer
                                     \
                                       decoder --- audio player

The demuxer is responsible to extract video and audio packets (read some
header, split the video stream into packets at correct boundaries, etc.).

The demuxed packets may be sent to a decoder (one per stream, to produce
frames) and to a recorder (receiving both video and audio stream to record a
single file). The packets are encoded on the device (by MediaCodec), but when
recording, they are muxed (asynchronously) into a container (MKV or MP4) on
the client side.

Video frames are sent to the screen/display to be rendered in the scrcpy window.
They may also be sent to a V4L2 sink.

Audio "frames" (an array of decoded samples) are sent to the audio player.

Controller

The controller is responsible to send control messages to the device. It
runs in a separate thread, to avoid I/O on the main thread.

On SDL event, received on the main thread, the input manager creates
appropriate control messages. It is responsible to convert SDL events to
Android events. It then pushes the control messages to a queue hold by the
controller. On its own thread, the controller takes messages from the queue,
that it serializes and sends to the client.

Protocol

The protocol between the client and the server must be considered internal: it
may (and will) change at any time for any reason. Everything may change (the
number of sockets, the order in which the sockets must be opened, the data
format on the wire…) from version to version. A client must always be run with a
matching server version.

This section documents the current protocol in scrcpy 4.0.

Connection

Firstly, the client sets up an adb tunnel:

bash
# By default, a reverse redirection: the computer listens, the device connects
adb reverse localabstract:scrcpy_<SCID> tcp:27183

# As a fallback (or if --force-adb forward is set), a forward redirection:
# the device listens, the computer connects
adb forward tcp:27183 localabstract:scrcpy_<SCID>

(<SCID> is a 31-bit random number, so that it does not fail when several
scrcpy instances start "at the same time" for the same device.)

Then, up to 3 sockets are opened, in that order:

  • a video socket
  • an audio socket
  • a control socket

Each one may be disabled (respectively by --no-video, --no-audio and
--no-control, directly or indirectly). For example, if --no-audio is set,
then the video socket is opened first, then the control socket.

On the first socket opened (whichever it is), if the tunnel is forward, then
a dummy byte is sent from the device to the client. This allows to detect a
connection error (the client connection does not fail as long as there is an adb
forward redirection, even if nothing is listening on the device side).

Still on this first socket, the device sends some metadata to
the client (currently only the device name, used as the window title, but there
might be other fields in the future).

You can read the client and server
code for more details.

Then each socket is used for its intended purpose.

Video and audio

On the video and audio sockets, the device first sends the codec id (u32).

Valid codec ids for video streams are:

  • "h264" (0x68323634)
  • "h265" (0x68323635)
  • "av1" (0x00617631)
  • "vp8" (0x00767038)
  • "vp9" (0x00767039)

Valid codec ids for audio streams are:

  • "opus" (0x6F707573)
  • "aac" (0x00616163)
  • "flac" (0x666C6163)
  • "raw" (0x00726177)

For the video stream, a session packet (12 bytes) is sent for each capture
session (a session changes when the device rotates):

text
byte 0   byte 1   byte 2   byte 3
    10000000 00000000 00000000 0000000R
    ^<------------------------------->^
    |               padding           |
     `- session packet flag            `- client resized flag

     byte 4   byte 5   byte 6   byte 7   byte 8   byte 9   byte 10  byte 11
    ........ ........ ........ ........ ........ ........ ........ ........
    <---------------------------------> <--------------------------------->
                video width                         video height

The "client resized" flag is used for flex displays to indicate that the frame
size changed due to a client resize request (see #6772).

For the audio stream, there are no session packets.

Then media packets are sent, each containing the payload produced by
MediaCodec, prefixed by a 12-byte frame header:

  • media packet flag (u1)
  • config packet flag (u1)
  • key frame flag (u1)
  • PTS (u61)
  • packet size (u32)

Here is a schema describing the frame header:

text
[. . . . . . . .|. . . .]. . . . . . . . . . . . . . . ...
     <-------------> <-----> <-----------------------------...
           PTS        packet        raw packet
                       size
     <--------------------->
           frame header

The most significant bits of the PTS are used for packet flags:

     byte 0   byte 1   byte 2   byte 3   byte 4   byte 5   byte 6   byte 7
    0CK..... ........ ........ ........ ........ ........ ........ ........
    ^^^<------------------------------------------------------------------>
    |||                                PTS
    || `- key frame
    | `-- config packet
     `--- media packet flag

     byte 8   byte 9   byte 10  byte 11
    ........ ........ ........ ........ ........ ........ . . .
    <---------------------------------> <---------------- . . .
               packet size                       raw packet

Session packets and media packets are distinguished by their first bit (the
MSB).

Controls

Controls messages are sent via a custom binary protocol.

The only documentation for this protocol is the set of unit tests on both sides:

Standalone server

Although the server is designed to work for the scrcpy client, it can be used
with any client which uses the same protocol.

For simplicity, some server-specific options have been added to produce raw
streams easily:

  • send_device_meta=false: disable device metadata (in practice, the device
    name) sent on the first socket
  • send_frame_meta=false: disable the 12-byte header for each packet
  • send_dummy_byte: disable the dummy byte sent on forward connections
  • send_stream_meta: disable codec and video size metadata
  • raw_stream: disable all the above

Concretely, here is how to expose a raw H.264 stream on a TCP socket:

bash
adb push scrcpy-server-v4.0 /data/local/tmp/scrcpy-server-manual.jar
adb forward tcp:1234 localabstract:scrcpy
adb shell CLASSPATH=/data/local/tmp/scrcpy-server-manual.jar \
    app_process / com.genymobile.scrcpy.Server 4.0 \
    tunnel_forward=true audio=false control=false cleanup=false \
    raw_stream=true max_size=1920

As soon as a client connects over TCP on port 1234, the device will start
streaming the video. For example, VLC can play the video (although you will
experience a very high latency, more details here):

bash
vlc -Idummy --demux=h264 --network-caching=0 tcp://localhost:1234

Hack

For more details, go read the code!

If you find a bug, or have an awesome idea to implement, please discuss and
contribute ;-)

Debug the server

The server is pushed to the device by the client on startup.

To debug it, enable the server debugger during configuration:

bash
meson setup x -Dserver_debugger=true
# or, if x is already configured
meson configure x -Dserver_debugger=true

Then recompile, and run scrcpy.

For Android < 11, it will start a debugger on port 5005 on the device and wait:
Redirect that port to the computer:

bash
adb forward tcp:5005 tcp:5005

For Android >= 11, first find the listening port:

bash
adb jdwp
# press Ctrl+C to interrupt

Then redirect the resulting PID:

bash
adb forward tcp:5005 jdwp:XXXX  # replace XXXX

In Android Studio, Run > Debug > Edit configurations... On the left, click
on +, Remote, and fill the form:

  • Host: localhost
  • Port: 5005

Then click on Debug.


File: doc/device.md

Device

Some command line arguments perform actions on the device itself while scrcpy is
running.

Keep active

To prevent the device from turning off due to inactivity, --keep-active
periodically signals user activity to the system:

bash
scrcpy --keep-active

Stay awake

To prevent the device from sleeping after a delay when the device is plugged
in
:

bash
scrcpy --stay-awake
scrcpy -w

The initial state is restored when scrcpy is closed.

If the device is not plugged in (i.e. only connected over TCP/IP),
--stay-awake has no effect (this is the Android behavior).

This changes the value of stay_on_while_plugged_in, setting which can be
changed manually:

bash
# get the current stay_on_while_plugged_in value
adb shell settings get global stay_on_while_plugged_in
# enable for AC/USB/wireless chargers
adb shell settings put global stay_on_while_plugged_in 7
# disable
adb shell settings put global stay_on_while_plugged_in 0

Screen off timeout

The Android screen automatically turns off after some delay.

To change this delay while scrcpy is running:

bash
scrcpy --screen-off-timeout=300  # 300 seconds (5 minutes)

The initial value is restored on exit.

It is possible to change this setting manually:

bash
# get the current screen_off_timeout value
adb shell settings get system screen_off_timeout
# set a new value (in milliseconds)
adb shell settings put system screen_off_timeout 30000

Note that the Android value is in milliseconds, but the scrcpy command line
argument is in seconds.

Turn screen off

It is possible to turn the device screen off while mirroring on start with a
command-line option:

bash
scrcpy --turn-screen-off
scrcpy -S   # short version

Or by pressing MOD+o at any time (see
shortcuts).

To turn it back on, press MOD+Shift+o.

On Android, the POWER button always turns the screen on. For convenience, if
POWER is sent via scrcpy (via right-click or MOD+p),
it will force to turn the screen off after a small delay (on a best effort
basis). The physical POWER button will still cause the screen to be turned on.

It can also be useful to prevent the device from sleeping:

bash
scrcpy --turn-screen-off --stay-awake
scrcpy -Sw   # short version

Since Android 15, it is possible to change this setting manually:

bash
# turn screen off (0 for main display)
adb shell cmd display power-off 0
# turn screen on
adb shell cmd display power-on 0

Show touches

For presentations, it may be useful to show physical touches (on the physical
device). Android exposes this feature in Developers options.

Scrcpy provides an option to enable this feature on start and restore the
initial value on exit:

bash
scrcpy --show-touches
scrcpy -t   # short version

Note that it only shows physical touches (by a finger on the device).

It is possible to change this setting manually:

bash
# get the current show_touches value
adb shell settings get system show_touches
# enable show_touches
adb shell settings put system show_touches 1
# disable show_touches
adb shell settings put system show_touches 0

Power off on close

To turn the device screen off when closing scrcpy:

bash
scrcpy --power-off-on-close

Power on on start

By default, on start, the device is powered on. To prevent this behavior:

bash
scrcpy --no-power-on

Start Android app

To list the Android apps installed on the device:

bash
scrcpy --list-apps

An app, selected by its package name, can be launched on start:

bash
scrcpy --start-app=org.mozilla.firefox

This feature can be used to run an app in a virtual
display
:

bash
scrcpy --new-display=1920x1080 --start-app=org.videolan.vlc

The app can be optionally forced-stop before being started, by adding a +
prefix:

bash
scrcpy --start-app=+org.mozilla.firefox

For convenience, it is also possible to select an app by its name, by adding a
? prefix:

bash
scrcpy --start-app=?firefox

But retrieving app names may take some time (sometimes several seconds), so
passing the package name is recommended.

The + and ? prefixes can be combined (in that order):

bash
scrcpy --start-app=+?firefox

File: doc/gamepad.md

Gamepad

Several gamepad input modes are available:

  • --gamepad=disabled (default)
  • --gamepad=uhid (or -G): simulates physical HID gamepads using the UHID
    kernel module on the device
  • --gamepad=aoa: simulates physical HID gamepads using the AOAv2 protocol

Physical gamepad simulation

Two modes allow to simulate physical HID gamepads on the device, one for each
physical gamepad plugged into the computer.

UHID

This mode simulates physical HID gamepads using the UHID kernel module on the
device.

To enable UHID gamepads, use:

bash
scrcpy --gamepad=uhid
scrcpy -G  # short version

Note: UHID may not work on old Android versions due to permission errors.

AOA

This mode simulates physical HID gamepads using the AOAv2 protocol.

To enable AOA gamepads, use:

bash
scrcpy --gamepad=aoa

Contrary to the other mode, it works at the USB level directly (so it only works
over USB).

It does not use the scrcpy server, and does not require adb (USB debugging).
Therefore, it is possible to control the device (but not mirror) even with USB
debugging disabled (see OTG).

Note: For some reason, in this mode, Android detects multiple physical gamepads
as a single misbehaving one. Use UHID if you need multiple gamepads.

Note: On Windows, it may only work in OTG mode, not while mirroring
(it is not possible to open a USB device if it is already open by another
process like the adb daemon).


File: doc/keyboard.md

Keyboard

Several keyboard input modes are available:

  • --keyboard=sdk (default)
  • --keyboard=uhid (or -K): simulates a physical HID keyboard using the UHID
    kernel module on the device
  • --keyboard=aoa: simulates a physical HID keyboard using the AOAv2 protocol
  • --keyboard=disabled

By default, sdk is used, but if you use scrcpy regularly, it is recommended to
use uhid and configure the keyboard layout once and for all.

SDK keyboard

In this mode (--keyboard=sdk, or if the parameter is omitted), keyboard input
events are injected at the Android API level. It works everywhere, but it is
limited to ASCII and some other characters.

Note that on some devices, an additional option must be enabled in developer
options for this keyboard mode to work. See
prerequisites.

Additional parameters (specific to --keyboard=sdk) described below allow to
customize the behavior.

Text injection preference

Two kinds of events are generated when typing text:

  • key events, signaling that a key is pressed or released;
  • text events, signaling that a text has been entered.

By default, numbers and "special characters" are inserted using text events, but
letters are injected using key events, so that the keyboard behaves as expected
in games (typically for WASD keys).

But this may cause issues. If you encounter such a problem, you
can inject letters as text (or just switch to UHID):

bash
scrcpy --prefer-text

(but this will break keyboard behavior in games)

On the contrary, you could force to always inject raw key events:

bash
scrcpy --raw-key-events

Key repeat

By default, holding a key down generates repeated key events. Ths can cause
performance problems in some games, where these events are useless anyway.

To avoid forwarding repeated key events:

bash
scrcpy --no-key-repeat

Physical keyboard simulation

Two modes allow to simulate a physical HID keyboard on the device.

To work properly, it is necessary to configure (once and for all) the keyboard
layout on the device to match that of the computer.

The configuration page can be opened in one of the following ways:

  • from the scrcpy window (when uhid or aoa is used), by pressing
    MOD+k (see shortcuts)
  • from the device, in Settings β†’ System β†’ Languages and input β†’ Physical
    devices
  • from a terminal on the computer, by executing adb shell am start -a android.settings.HARD_KEYBOARD_SETTINGS

From this configuration page, it is also possible to enable or disable on-screen
keyboard.

UHID

This mode simulates a physical HID keyboard using the UHID kernel module on the
device.

To enable UHID keyboard, use:

bash
scrcpy --keyboard=uhid
scrcpy -K  # short version

Once the keyboard layout is configured (see above), it is the best mode for
using the keyboard while mirroring:

  • it works for all characters and IME (contrary to --keyboard=sdk)
  • the on-screen keyboard can be disabled (contrary to --keyboard=sdk)
  • it works over TCP/IP (wirelessly) (contrary to --keyboard=aoa)
  • there are no issues on Windows (contrary to --keyboard=aoa)

One drawback is that it may not work on old Android versions due to permission
errors.

AOA

This mode simulates a physical HID keyboard using the AOAv2 protocol.

To enable AOA keyboard, use:

bash
scrcpy --keyboard=aoa

Contrary to the other modes, it works at the USB level directly (so it only
works over USB).

It does not use the scrcpy server, and does not require adb (USB debugging).
Therefore, it is possible to control the device (but not mirror) even with USB
debugging disabled (see OTG).

Note: On Windows, it may only work in OTG mode, not while mirroring
(it is not possible to open a USB device if it is already open by another
process like the adb daemon).