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 containsscrcpy.
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:
- audio forwarding (Android 11+)
- recording
- virtual display
- mirroring with Android device screen off
- copy-paste in both directions
- configurable quality
- camera mirroring (Android 12+)
- mirroring as a webcam (V4L2) (Linux-only)
- physical keyboard and mouse simulation (HID)
- gamepad support
- OTG mode
- and moreβ¦
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:
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
- Linux
- Windows (read how to run)
- macOS
Must-know tips
- Reducing resolution may greatly improve performance
(scrcpy -m1024) - Right-click triggers
BACK - Middle-click triggers
HOME - Alt+f toggles fullscreen
- There are many other shortcuts
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:bashscrcpy --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 versionStart VLC in a new virtual display (separate from the device display):
bashscrcpy --new-display=1920x1080 --start-app=org.videolan.vlcStart 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:bashscrcpy --new-display -x --keep-active --start-app=org.videolan.vlc --video-codec=h265 -b16MRecord the device camera in H.265 at 1920x1080 (and microphone) to an MP4
file:bashscrcpy --video-source=camera --video-codec=h265 --camera-size=1920x1080 --record=file.mp4Capture the device front camera and expose it as a webcam on the computer (on
Linux):bashscrcpy --video-source=camera --camera-size=1920x1080 --camera-facing=front --v4l2-sink=/dev/video2 --no-playbackControl the device without mirroring by simulating a physical keyboard and
mouse (USB debugging not required):bashscrcpy --otgControl the device using gamepads plugged into the computer:
bashscrcpy --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:
- Connection
- Video
- Audio
- Control
- Keyboard
- Mouse
- Gamepad
- Device
- Window
- Recording
- Virtual display
- Tunnels
- OTG
- Camera
- Video4Linux
- Shortcuts
Resources
- FAQ
- Translations (not necessarily up to date)
- Build instructions
- Developers
- Verify release signatures
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:
- Reddit:
r/scrcpy - BlueSky:
@scrcpy.bsky.social - Twitter:
@scrcpy_app
Donate
I'm @rom1v, the author and maintainer of scrcpy.
If you appreciate this application, you can support my open source
work:
License
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:
scrcpy --no-audioTo disable only the audio playback, see no playback.
Audio only
To play audio only, disable video and control:
scrcpy --no-video --no-controlTo play audio without a window:
# --no-video and --no-control are implied by --no-window
scrcpy --no-window
# interrupt with Ctrl+CWithout video, the audio latency is typically not critical, so it might be
interesting to add buffering to minimize glitches:
scrcpy --no-video --audio-buffer=200Source
By default, the device audio output is forwarded.
It is possible to capture the device microphone instead:
scrcpy --audio-source=micFor example, to use the device as a dictaphone and record a capture directly on
the computer:
scrcpy --audio-source=mic --no-video --no-playback --record=file.opusMany sources are available:
output(default): forwards the whole audio output, and disables playback on the device (mapped toREMOTE_SUBMIX).playback: captures the audio playback (Android apps can opt-out, so the whole output is not necessarily captured).mic: captures the microphone (mapped toMIC).mic-unprocessed: captures the microphone unprocessed (raw) sound (mapped toUNPROCESSED).mic-camcorder: captures the microphone tuned for video recording, with the same orientation as the camera if available (mapped toCAMCORDER).mic-voice-recognition: captures the microphone tuned for voice recognition (mapped toVOICE_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 toVOICE_COMMUNICATION).voice-call: captures voice call (mapped toVOICE_CALL).voice-call-uplink: captures voice call uplink only (mapped toVOICE_UPLINK).voice-call-downlink: captures voice call downlink only (mapped toVOICE_DOWNLINK).voice-performance: captures audio meant to be processed for live performance (karaoke), includes both the microphone and the device playback (mapped toVOICE_PERFORMANCE).
Duplication
An alternative device audio capture method is also available (only for Android
13 and above):
scrcpy --audio-source=playbackThis audio source supports keeping the audio playing on the device while
mirroring, with --audio-dup:
scrcpy --audio-source=playback --audio-dup
# or simply:
scrcpy --audio-dup # --audio-source=playback is impliedHowever, 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):
scrcpy --audio-codec=opus # default
scrcpy --audio-codec=aac
scrcpy --audio-codec=flac
scrcpy --audio-codec=rawIn 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:
scrcpy --audio-codec=flac --audio-codec-options=flac-compression-level=8Encoder
Several encoders may be available on the device. They can be listed by:
scrcpy --list-encodersTo select a specific encoder:
scrcpy --audio-codec=opus --audio-encoder='c2.android.opus.encoder'Bit rate
The default audio bit rate is 128Kbps. To change it:
scrcpy --audio-bit-rate=64K
scrcpy --audio-bit-rate=64000 # equivalentThis 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:
scrcpy --audio-buffer=40 # smaller than default
scrcpy --audio-buffer=100 # higher than defaultNote 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:
scrcpy --video-buffer=200 --audio-buffer=200It 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:
scrcpy --audio-output-buffer=5File: 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 indevwill 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.exeAdbWinApi.dllAdbWinUsbApi.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
# 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-jdkOn old versions (like Ubuntu 16.04), meson is too old. In that case, install
it from pip3:
sudo apt install python3-pip
pip3 install mesonFedora
# 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-develWindows
Cross-compile from Linux
This is the preferred method (and the way the release is built).
From Debian, install mingw:
sudo apt install mingw-w64 mingw-w64-tools libz-mingw-w64-devYou also need the JDK to build the server:
sudo apt install openjdk-17-jdkThen generate the releases:
./release.shIt 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:
# 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-mesonFor a 32 bits version, replace x86_64 by i686:
# 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-mesonJava (>= 7) is not available in MSYS2, so if you plan to build the server,
install it manually and make it available from the PATH:
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:
# runtime dependencies
brew install sdl3 ffmpeg libusb
# client build dependencies
brew install pkg-config mesonAdditionally, if you want to build the server, install Java 17 from Caskroom, and
make it available from the PATH:
brew install openjdk@17
export JAVA_HOME="$(/usr/libexec/java_home --version 1.17)"
export PATH="$JAVA_HOME/bin:$PATH"Docker
Common steps
As a non-root user, clone the project:
git clone https://github.com/Genymobile/scrcpy
cd scrcpyBuild
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:
# Linux
export ANDROID_SDK_ROOT=~/Android/Sdk
# Mac
export ANDROID_SDK_ROOT=~/Library/Android/sdk
# Windows
set ANDROID_SDK_ROOT=%LOCALAPPDATA%\Android\sdkThen, build:
meson setup x --buildtype=release --strip -Db_lto=true
ninja -Cx # DO NOT RUN AS ROOTNote: 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:
meson setup x --buildtype=release --strip -Db_lto=true \
-Dprebuilt_server=/path/to/scrcpy-server
ninja -Cx # DO NOT RUN AS ROOTThe server only works with a matching client version (this server works with themaster branch).
Run without installing:
./run x [options]Install
After a successful build, you can install scrcpy on the system:
sudo ninja -Cx install # without sudo on WindowsThis 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
sudo ninja -Cx uninstall # without sudo on WindowsFile: doc/camera.md
Camera
Camera mirroring is supported for devices with Android 12 or higher.
To capture the camera instead of the device screen:
scrcpy --video-source=cameraBy default, it automatically switches audio source to
microphone (as if --audio-source=mic were also passed).
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 outputAudio can be disabled:
# 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.mp4List
To list the cameras available (with their declared valid sizes and frame rates):
scrcpy --list-cameras
scrcpy --list-camera-sizesNote 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):
scrcpy --video-source=camera --camera-id=0Alternatively, the camera may be selected automatically:
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 cameraIf --camera-id is specified, then --camera-facing is forbidden (the id
already determines the camera):
scrcpy --video-source=camera --camera-id=0 --camera-facing=front # errorSize selection
It is possible to pass an explicit camera size:
scrcpy --video-source=camera --camera-size=1920x1080The given size may be listed among the declared valid sizes
(--list-camera-sizes), but may also be anything else (some devices support
arbitrary sizes):
scrcpy --video-source=camera --camera-size=1840x444Alternatively, a declared valid size (among the ones listed bylist-camera-sizes) may be selected automatically.
Two constraints are supported:
-m/--max-size(already used for display mirroring), for example-m1920;--camera-arto specify an aspect ratio (<num>:<den>,<value>orsensor).
Some examples:
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 ratioIf --camera-size is specified, then -m/--max-size and --camera-ar are
forbidden (the size is determined by the value given explicitly):
scrcpy --video-source=camera --camera-size=1920x1080 -m3000 # errorRotation
To rotate the captured video, use the video orientation
option:
scrcpy --video-source=camera --camera-size=1920x1080 --orientation=90Frame rate
By default, camera is captured at Android's default frame rate (30 fps).
To configure a different frame rate:
scrcpy --video-source=camera --camera-fps=60High 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.
scrcpy --video-source=camera --camera-size=1920x1080 --camera-high-speed --camera-fps=240Brace 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):
scrcpy --video-source=camera --camera-{facing=back,ar=16:9,high-speed,fps=120}This will be expanded as:
scrcpy --video-source=camera --camera-facing=back --camera-ar=16:9 --camera-high-speed --camera-fps=120Torch
The camera torch can be turned on at startup by --camera-torch:
scrcpy --video-source=camera --camera-torchIt 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:
scrcpy --video-source=camera --camera-zoom=1.5It 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):
# in bash
export ANDROID_SERIAL=0123456789abcdef
scrcpy:: in cmd
set ANDROID_SERIAL=0123456789abcdef
scrcpy# in PowerShell
$env:ANDROID_SERIAL = '0123456789abcdef'
scrcpyTCP/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:
scrcpy --tcpip # without argumentsIt 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:
scrcpy --tcpip=192.168.1.1 # default port is 5555
scrcpy --tcpip=192.168.1.1:5555Prefix the address with a '+' to force a reconnection:
scrcpy --tcpip=+192.168.1.1Manual
Alternatively, it is possible to enable the TCP/IP connection manually usingadb:
Plug the device into a USB port on your computer.
Connect the device to the same Wi-Fi network as your computer.
Get your device IP address, in Settings β About phone β Status, or by
executing this command:bashadb shell ip route | awk '{print $9}'Enable
adbover TCP/IP on your device:adb tcpip 5555.Unplug your device.
Connect to your device:
adb connect DEVICE_IP:5555(replaceDEVICE_IP
with the device IP address you found).Run
scrcpyas usual.Run
adb disconnectonce 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:
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):
scrcpy --no-control
scrcpy -n # short versionKeyboard and mouse
Control only
To control the device without mirroring:
scrcpy --no-video --no-audioBy default, the mouse is disabled when video playback is turned off.
To control the device using a relative mouse, enable UHID mouse mode:
scrcpy --no-video --no-audio --mouse=uhid
scrcpy --no-video --no-audio -M # short versionTo also use a UHID keyboard, set it explicitly:
scrcpy --no-video --no-audio --mouse=uhid --keyboard=uhid
scrcpy --no-video --no-audio -MK # short versionTo use AOA instead (over USB only):
scrcpy --no-video --no-audio --keyboard=aoa --mouse=aoaCopy-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:
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
scrcpybinary), 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 asshell 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 toclasses.dex, pushed to the device in /data/local/tmp, then it can be run
with:
adb shell CLASSPATH=/data/local/tmp/classes.dex app_process / my.package.MainClassThe 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 containingclasses.dex (e.g. an APK). For simplicity, and to benefit from the gradle
build system, the server is built to an (unsigned) APK (renamed toscrcpy-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:
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.0The 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:
# 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=1920Components
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 flagKEY_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:
- scrcpy in "normal" mode (
scrcpy.c) - scrcpy in OTG mode (
scrcpy_otg.c)
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:
V4L2 sink
/
decoder
/ \
VIDEO -------------> demuxer display
\
recorder
/
AUDIO -------------> demuxer
\
decoder --- audio playerThe 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:
# 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):
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 heightThe "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 byMediaCodec, 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:
[. . . . . . . .|. . . .]. . . . . . . . . . . . . . . ...
<-------------> <-----> <-----------------------------...
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 packetSession 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:
ControlMessage(from client to device): serialization
| deserializationDeviceMessage(from device to client) serialization
| deserialization
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 socketsend_frame_meta=false: disable the 12-byte header for each packetsend_dummy_byte: disable the dummy byte sent on forward connectionssend_stream_meta: disable codec and video size metadataraw_stream: disable all the above
Concretely, here is how to expose a raw H.264 stream on a TCP socket:
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=1920As 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):
vlc -Idummy --demux=h264 --network-caching=0 tcp://localhost:1234Hack
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:
meson setup x -Dserver_debugger=true
# or, if x is already configured
meson configure x -Dserver_debugger=trueThen 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:
adb forward tcp:5005 tcp:5005For Android >= 11, first find the listening port:
adb jdwp
# press Ctrl+C to interruptThen redirect the resulting PID:
adb forward tcp:5005 jdwp:XXXX # replace XXXXIn 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:
scrcpy --keep-activeStay awake
To prevent the device from sleeping after a delay when the device is plugged
in:
scrcpy --stay-awake
scrcpy -wThe 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:
# 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 0Screen off timeout
The Android screen automatically turns off after some delay.
To change this delay while scrcpy is running:
scrcpy --screen-off-timeout=300 # 300 seconds (5 minutes)The initial value is restored on exit.
It is possible to change this setting manually:
# 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 30000Note 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:
scrcpy --turn-screen-off
scrcpy -S # short versionOr 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, ifPOWER 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:
scrcpy --turn-screen-off --stay-awake
scrcpy -Sw # short versionSince Android 15, it is possible to change this setting manually:
# turn screen off (0 for main display)
adb shell cmd display power-off 0
# turn screen on
adb shell cmd display power-on 0Show 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:
scrcpy --show-touches
scrcpy -t # short versionNote that it only shows physical touches (by a finger on the device).
It is possible to change this setting manually:
# 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 0Power off on close
To turn the device screen off when closing scrcpy:
scrcpy --power-off-on-closePower on on start
By default, on start, the device is powered on. To prevent this behavior:
scrcpy --no-power-onStart Android app
To list the Android apps installed on the device:
scrcpy --list-appsAn app, selected by its package name, can be launched on start:
scrcpy --start-app=org.mozilla.firefoxThis feature can be used to run an app in a virtual
display:
scrcpy --new-display=1920x1080 --start-app=org.videolan.vlcThe app can be optionally forced-stop before being started, by adding a +
prefix:
scrcpy --start-app=+org.mozilla.firefoxFor convenience, it is also possible to select an app by its name, by adding a? prefix:
scrcpy --start-app=?firefoxBut 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):
scrcpy --start-app=+?firefoxFile: 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:
scrcpy --gamepad=uhid
scrcpy -G # short versionNote: 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:
scrcpy --gamepad=aoaContrary 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):
scrcpy --prefer-text(but this will break keyboard behavior in games)
On the contrary, you could force to always inject raw key events:
scrcpy --raw-key-eventsKey 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:
scrcpy --no-key-repeatPhysical 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
uhidoraoais 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:
scrcpy --keyboard=uhid
scrcpy -K # short versionOnce 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:
scrcpy --keyboard=aoaContrary 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).