mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-02-23 23:04:43 +01:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1de8506a0d | ||
|
|
71f50fb697 | ||
|
|
82efff34e8 | ||
|
|
860006e082 | ||
|
|
57eaf05289 | ||
|
|
dd2a5c1ecf | ||
|
|
66ec252893 | ||
|
|
b13d25b9f4 | ||
|
|
b449c09442 | ||
|
|
8b84492830 | ||
|
|
4d50832f8e | ||
|
|
e0e8dfeb3b | ||
|
|
f4d6449af7 | ||
|
|
64963fff62 | ||
|
|
b7d9b8739c | ||
|
|
88f6b43c8f | ||
|
|
324a264233 | ||
|
|
3bb2cda955 | ||
|
|
35298bb0c6 | ||
|
|
73c332e3e4 | ||
|
|
15014f2689 | ||
|
|
29b5c5b8f4 | ||
|
|
88ee6bc928 | ||
|
|
35e9a64c34 | ||
|
|
9cac38fe58 | ||
|
|
301c52b603 | ||
|
|
f00c6c5b13 | ||
|
|
3b3803da0d | ||
|
|
f5cf6c1b2c | ||
|
|
2573df9727 | ||
|
|
c65cb36d3b | ||
|
|
821ec9843c | ||
|
|
f16bd88802 | ||
|
|
f3e8834a3c | ||
|
|
080df5eb5d | ||
|
|
047179f232 | ||
|
|
2992dda497 | ||
|
|
6406f64edc | ||
|
|
f3f19d4e1d | ||
|
|
d744837f13 | ||
|
|
f7bc0bd5b5 | ||
|
|
8a3c6a3ae7 | ||
|
|
c530d95881 | ||
|
|
0b1e59186f | ||
|
|
e69f6f710d | ||
|
|
b858204786 | ||
|
|
e8510a8cc3 | ||
|
|
f9a63ec272 | ||
|
|
f6d0893316 | ||
|
|
ed65cd72fd | ||
|
|
70599998eb | ||
|
|
e87cd175cc | ||
|
|
14b15ceb06 | ||
|
|
b9bb4ff740 | ||
|
|
cc4a015256 | ||
|
|
8476b4aab8 | ||
|
|
a1491862e4 |
81
FAQ.md
Normal file
81
FAQ.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
## Common issues
|
||||
|
||||
The application is very young, it is not unlikely that you encounter problems
|
||||
with it.
|
||||
|
||||
Here are the common reported problems and their status.
|
||||
|
||||
### On Windows, I have no output in the console
|
||||
|
||||
When run in `cmd.exe`, the application does not print anything. Even `scrcpy
|
||||
--help` have no output. We don't know why yet.
|
||||
|
||||
However, if you run the very same `scrcpy.exe` from
|
||||
[MSYS2](https://www.msys2.org/) (`mingw64`), then it correctly prints output.
|
||||
|
||||
As a workaround, redirect outputs to files, so that you can read the files
|
||||
afterwards:
|
||||
|
||||
```bash
|
||||
scrcpy >stdout 2>stderr
|
||||
type stdout
|
||||
type stderr
|
||||
```
|
||||
|
||||
_Note that all SDL logs are printed to stderr._
|
||||
|
||||
|
||||
### On Windows, when I start the application, nothing happens
|
||||
|
||||
The previous problem does not help to get a clue about the cause.
|
||||
|
||||
The most common is your device not being detected by `adb`, or is unauthorized.
|
||||
Check everything is ok by calling:
|
||||
|
||||
adb devices
|
||||
|
||||
Windows may need some [drivers] to detect your device.
|
||||
|
||||
[drivers]: https://developer.android.com/studio/run/oem-usb.html
|
||||
|
||||
If you still encounter problems, please see [issue 9].
|
||||
|
||||
[issue 9]: https://github.com/Genymobile/scrcpy/issues/9
|
||||
|
||||
|
||||
### I get a black screen for some applications like Silence
|
||||
|
||||
This is expected, they requested to protect the screen.
|
||||
|
||||
In [Silence], you can disable it in settings → Privacy → Screen security.
|
||||
|
||||
[silence]: https://f-droid.org/en/packages/org.smssecure.smssecure/
|
||||
|
||||
See [issue 36].
|
||||
|
||||
[issue 36]: https://github.com/Genymobile/scrcpy/issues/36
|
||||
|
||||
|
||||
### Mouse clicks do not work
|
||||
|
||||
On some devices, you may need to enable an option to allow [simulating input].
|
||||
|
||||
[simulating input]: https://github.com/Genymobile/scrcpy/issues/70#issuecomment-373286323
|
||||
|
||||
|
||||
### Mouse clicks at wrong location
|
||||
|
||||
On MacOS, with HiDPI support and multiple screens, input location are wrongly
|
||||
scaled. See [issue 15].
|
||||
|
||||
[issue 15]: https://github.com/Genymobile/scrcpy/issues/15
|
||||
|
||||
A workaround is to build with HiDPI support disabled:
|
||||
|
||||
```bash
|
||||
meson x --buildtype release -Dhidpi_support=false
|
||||
```
|
||||
|
||||
However, the video will be displayed at lower resolution.
|
||||
236
README.md
236
README.md
@@ -2,34 +2,35 @@
|
||||
|
||||
This application provides display and control of Android devices connected on
|
||||
USB. It does not require any _root_ access. It works on _GNU/Linux_, _Windows_
|
||||
and _Mac OS_.
|
||||
and _MacOS_.
|
||||
|
||||

|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
The Android part requires at least API 21 (Android 5.0).
|
||||
The Android part requires at least API 19 (Android 4.4).
|
||||
|
||||
You need [adb] (recent enough so that `adb reverse` is implemented, it works
|
||||
with 1.0.36). It is available in the [Android SDK platform
|
||||
tools][platform-tools], on packaged in your distribution (`android-adb-tools`).
|
||||
You need [adb]. It is available in the [Android SDK platform
|
||||
tools][platform-tools], or packaged in your distribution (`android-adb-tools`).
|
||||
|
||||
On Windows, just download the [platform-tools][platform-tools-windows] and
|
||||
extract the following files to a directory accessible from your `PATH`:
|
||||
On Windows, if you use the [prebuilt application](#windows), it is already
|
||||
included. Otherwise, just download the [platform-tools][platform-tools-windows]
|
||||
and extract the following files to a directory accessible from your `PATH`:
|
||||
- `adb.exe`
|
||||
- `AdbWinApi.dll`
|
||||
- `AdbWinUsbApi.dll`
|
||||
|
||||
Make sure you [enabled adb debugging][enable-adb] on your device(s).
|
||||
|
||||
The client requires [FFmpeg] and [LibSDL2].
|
||||
|
||||
[adb]: https://developer.android.com/studio/command-line/adb.html
|
||||
[enable-adb]: https://developer.android.com/studio/command-line/adb.html#Enabling
|
||||
[platform-tools]: https://developer.android.com/studio/releases/platform-tools.html
|
||||
[platform-tools-windows]: https://dl.google.com/android/repository/platform-tools-latest-windows.zip
|
||||
|
||||
The client requires _FFmpeg_ and _LibSDL2_.
|
||||
|
||||
[ffmpeg]: https://en.wikipedia.org/wiki/FFmpeg
|
||||
[LibSDL2]: https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
|
||||
|
||||
## Build and install
|
||||
|
||||
@@ -37,15 +38,44 @@ The client requires _FFmpeg_ and _LibSDL2_.
|
||||
|
||||
#### Linux
|
||||
|
||||
Install the required packages from your package manager (here, for Debian):
|
||||
Install the required packages from your package manager.
|
||||
|
||||
# runtime dependencies
|
||||
sudo apt install ffmpeg libsdl2-2.0.0
|
||||
##### Debian/Ubuntu
|
||||
|
||||
# build dependencies
|
||||
sudo apt install make gcc openjdk-8-jdk pkg-config meson zip \
|
||||
libavcodec-dev libavformat-dev libavutil-dev \
|
||||
libsdl2-dev
|
||||
```bash
|
||||
# runtime dependencies
|
||||
sudo apt install ffmpeg libsdl2-2.0.0
|
||||
|
||||
# client build dependencies
|
||||
sudo apt install make gcc pkg-config meson \
|
||||
libavcodec-dev libavformat-dev libavutil-dev \
|
||||
libsdl2-dev
|
||||
|
||||
# server build dependencies
|
||||
sudo apt install openjdk-8-jdk
|
||||
```
|
||||
|
||||
##### 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 SDL2-devel ffms2-devel meson gcc make
|
||||
|
||||
# server build dependencies
|
||||
sudo dnf install java
|
||||
```
|
||||
|
||||
##### Arch Linux
|
||||
|
||||
Two [AUR] packages have been created by users:
|
||||
|
||||
- [`scrcpy`](https://aur.archlinux.org/packages/scrcpy/)
|
||||
- [`scrcpy-prebuiltserver`](https://aur.archlinux.org/packages/scrcpy-prebuiltserver/)
|
||||
|
||||
[AUR]: https://wiki.archlinux.org/index.php/Arch_User_Repository
|
||||
|
||||
|
||||
#### Windows
|
||||
@@ -53,10 +83,10 @@ Install the required packages from your package manager (here, for Debian):
|
||||
For Windows, for simplicity, a prebuilt archive with all the dependencies
|
||||
(including `adb`) is available:
|
||||
|
||||
- [`scrcpy-windows-with-deps-v1.0.zip`][direct-windows-with-deps].
|
||||
_(SHA-256: bc4bf32600e8548cdce490f94bed5dcba0006cdd38aff95748972e5d9877dd62)_
|
||||
- [`scrcpy-windows-with-deps-v1.1.zip`][direct-windows-with-deps].
|
||||
_(SHA-256: 27eb36c15937601d1062c1dc0b45faae0e06fefea2019aadeb4fa7f76a07bb4c)_
|
||||
|
||||
[direct-windows-with-deps]: https://github.com/Genymobile/scrcpy/releases/download/v1.0/scrcpy-windows-with-deps-v1.0.zip
|
||||
[direct-windows-with-deps]: https://github.com/Genymobile/scrcpy/releases/download/v1.1/scrcpy-windows-with-deps-v1.1.zip
|
||||
|
||||
_(It's just a portable version including _dll_ copied from MSYS2.)_
|
||||
|
||||
@@ -65,22 +95,24 @@ project. From an MSYS2 terminal, install the required packages:
|
||||
|
||||
[MSYS2]: http://www.msys2.org/
|
||||
|
||||
# runtime dependencies
|
||||
pacman -S mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-ffmpeg
|
||||
```bash
|
||||
# runtime dependencies
|
||||
pacman -S mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-ffmpeg
|
||||
|
||||
# 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 \
|
||||
zip
|
||||
# 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
|
||||
```
|
||||
|
||||
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`:
|
||||
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
|
||||
```bash
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
```
|
||||
|
||||
#### Mac OS
|
||||
|
||||
@@ -88,17 +120,27 @@ Use [Homebrew] to install the packages:
|
||||
|
||||
[Homebrew]: https://brew.sh/
|
||||
|
||||
# runtime dependencies
|
||||
brew install sdl2 ffmpeg
|
||||
```bash
|
||||
# runtime dependencies
|
||||
brew install sdl2 ffmpeg
|
||||
|
||||
# build dependencies
|
||||
brew install gcc pkg-config meson zip
|
||||
# client build dependencies
|
||||
brew install pkg-config meson
|
||||
```
|
||||
|
||||
Java (>= 7) is not available in Homebrew, so if you plan to build the server,
|
||||
install it manually and make it available from the `PATH`:
|
||||
Additionally, if you want to build the server, install Java 8 from Caskroom, and
|
||||
make it avaliable from the `PATH`:
|
||||
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
```bash
|
||||
brew tap caskroom/versions
|
||||
brew cask install java8
|
||||
export JAVA_HOME="$(/usr/libexec/java_home --version 1.8)"
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
```
|
||||
|
||||
#### Docker
|
||||
|
||||
See [pierlon/scrcpy-docker](https://github.com/pierlon/scrcpy-docker).
|
||||
|
||||
### Common steps
|
||||
|
||||
@@ -107,21 +149,36 @@ its directory. For example:
|
||||
|
||||
[Android SDK]: https://developer.android.com/studio/index.html
|
||||
|
||||
export ANDROID_HOME=~/android/sdk
|
||||
```bash
|
||||
export ANDROID_HOME=~/android/sdk
|
||||
```
|
||||
|
||||
Then, build `scrcpy`:
|
||||
Clone the project:
|
||||
|
||||
meson x --buildtype release --strip -Db_lto=true
|
||||
cd x
|
||||
ninja
|
||||
```bash
|
||||
git clone https://github.com/Genymobile/scrcpy
|
||||
cd scrcpy
|
||||
```
|
||||
|
||||
Then, build:
|
||||
|
||||
```bash
|
||||
meson x --buildtype release --strip -Db_lto=true
|
||||
cd x
|
||||
ninja
|
||||
```
|
||||
|
||||
You can test it from here:
|
||||
|
||||
ninja run
|
||||
```bash
|
||||
ninja run
|
||||
```
|
||||
|
||||
Or you can install it on the system:
|
||||
|
||||
sudo ninja install # without sudo on Windows
|
||||
```bash
|
||||
sudo ninja install # without sudo on Windows
|
||||
```
|
||||
|
||||
This installs two files:
|
||||
|
||||
@@ -137,22 +194,23 @@ Since the server binary, that will be pushed to the Android device, does not
|
||||
depend on your system and architecture, you may want to use the prebuilt binary
|
||||
instead:
|
||||
|
||||
- [`scrcpy-server-v1.0.jar`][direct-scrcpy-server].
|
||||
_(SHA-256: b573b06a6072476b85b6308e3ad189f2665ad5be4f8ca3a6b9ec81d64df20558)_
|
||||
- [`scrcpy-server-v1.1.jar`][direct-scrcpy-server].
|
||||
_(SHA-256: 14826512bf38447ec94adf3b531676ce038d19e7e06757fb4e537882b17e77b3)_
|
||||
|
||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.0/scrcpy-server-v1.0.jar
|
||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.1/scrcpy-server-v1.1.jar
|
||||
|
||||
In that case, the build does not require Java or the Android SDK.
|
||||
|
||||
Download the prebuilt server somewhere, and specify its path during the Meson
|
||||
configuration:
|
||||
|
||||
meson x --buildtype release --strip -Db_lto=true \
|
||||
-Dprebuilt_server=/path/to/scrcpy-server.jar
|
||||
cd x
|
||||
ninja
|
||||
sudo ninja install
|
||||
|
||||
```bash
|
||||
meson x --buildtype release --strip -Db_lto=true \
|
||||
-Dprebuilt_server=/path/to/scrcpy-server.jar
|
||||
cd x
|
||||
ninja
|
||||
sudo ninja install
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
@@ -160,50 +218,70 @@ _At runtime, `adb` must be accessible from your `PATH`._
|
||||
|
||||
If everything is ok, just plug an Android device, and execute:
|
||||
|
||||
scrcpy
|
||||
```bash
|
||||
scrcpy
|
||||
```
|
||||
|
||||
It accepts command-line arguments, listed by:
|
||||
|
||||
scrcpy --help
|
||||
```bash
|
||||
scrcpy --help
|
||||
```
|
||||
|
||||
For example, to decrease video bitrate to 2Mbps (default is 8Mbps):
|
||||
|
||||
scrcpy -b 2M
|
||||
```bash
|
||||
scrcpy -b 2M
|
||||
```
|
||||
|
||||
To limit the video dimensions (e.g. if the device is 2540×1440, but the host
|
||||
screen is smaller, or cannot decode such a high definition):
|
||||
|
||||
scrcpy -m 1024
|
||||
```bash
|
||||
scrcpy -m 1024
|
||||
```
|
||||
|
||||
If several devices are listed in `adb devices`, you must specify the _serial_:
|
||||
|
||||
scrcpy -s 0123456789abcdef
|
||||
```bash
|
||||
scrcpy -s 0123456789abcdef
|
||||
```
|
||||
|
||||
To show physical touches while scrcpy is running:
|
||||
|
||||
```bash
|
||||
scrcpy -t
|
||||
```
|
||||
|
||||
To run without installing:
|
||||
|
||||
./run x [options]
|
||||
```bash
|
||||
./run x [options]
|
||||
```
|
||||
|
||||
(where `x` is your build directory).
|
||||
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Action | Shortcut |
|
||||
| -------------------------------------- |:---------------------------- |
|
||||
| switch fullscreen mode | `Ctrl`+`f` |
|
||||
| resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` |
|
||||
| resize window to remove black borders | `Ctrl`+`x` |
|
||||
| click on `HOME` | `Ctrl`+`h` \| _Middle-click_ |
|
||||
| click on `BACK` | `Ctrl`+`b` \| _Right-click¹_ |
|
||||
| click on `APP_SWITCH` | `Ctrl`+`m` |
|
||||
| click on `VOLUME_UP` | `Ctrl`+`+` |
|
||||
| click on `VOLUME_DOWN` | `Ctrl`+`-` |
|
||||
| click on `POWER` | `Ctrl`+`p` |
|
||||
| turn screen on | _Right-click¹_ |
|
||||
| paste computer clipboard to device | `Ctrl`+`v` |
|
||||
| enable/disable FPS counter (on stdout) | `Ctrl`+`i` |
|
||||
| Action | Shortcut |
|
||||
| -------------------------------------- |:---------------------------- |
|
||||
| switch fullscreen mode | `Ctrl`+`f` |
|
||||
| resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` |
|
||||
| resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ |
|
||||
| click on `HOME` | `Ctrl`+`h` \| _Middle-click_ |
|
||||
| click on `BACK` | `Ctrl`+`b` \| _Right-click²_ |
|
||||
| click on `APP_SWITCH` | `Ctrl`+`s` |
|
||||
| click on `MENU` | `Ctrl`+`m` |
|
||||
| click on `VOLUME_UP` | `Ctrl`+`+` |
|
||||
| click on `VOLUME_DOWN` | `Ctrl`+`-` |
|
||||
| click on `POWER` | `Ctrl`+`p` |
|
||||
| turn screen on | _Right-click²_ |
|
||||
| paste computer clipboard to device | `Ctrl`+`v` |
|
||||
| enable/disable FPS counter (on stdout) | `Ctrl`+`i` |
|
||||
|
||||
_¹Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||
_¹Double-click on black borders to remove them._
|
||||
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||
|
||||
|
||||
## Why _scrcpy_?
|
||||
@@ -216,6 +294,11 @@ A colleague challenged me to find a name as unpronounceable as [gnirehtet].
|
||||
[`strcpy`]: http://man7.org/linux/man-pages/man3/strcpy.3.html
|
||||
|
||||
|
||||
## Common issues
|
||||
|
||||
See the [FAQ](FAQ.md).
|
||||
|
||||
|
||||
## Developers
|
||||
|
||||
Read the [developers page].
|
||||
@@ -239,6 +322,7 @@ Read the [developers page].
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## Article
|
||||
## Articles
|
||||
|
||||
- [Introducing scrcpy](https://blog.rom1v.com/2018/03/introducing-scrcpy/)
|
||||
- [Scrcpy now works wirelessly](https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/)
|
||||
|
||||
@@ -42,7 +42,7 @@ conf = configuration_data()
|
||||
conf.set('BUILD_DEBUG', get_option('buildtype') == 'debug')
|
||||
|
||||
# the version, updated on release
|
||||
conf.set_quoted('SCRCPY_VERSION', '1.0')
|
||||
conf.set_quoted('SCRCPY_VERSION', '1.1')
|
||||
|
||||
# the prefix used during configuration (meson --prefix=PREFIX)
|
||||
conf.set_quoted('PREFIX', get_option('prefix'))
|
||||
@@ -85,7 +85,8 @@ conf.set('HIDPI_SUPPORT', get_option('hidpi_support'))
|
||||
|
||||
configure_file(configuration: conf, output: 'config.h')
|
||||
|
||||
executable('scrcpy', src, dependencies: dependencies, install: true)
|
||||
src_dir = include_directories('src')
|
||||
executable('scrcpy', src, dependencies: dependencies, include_directories: src_dir, install: true)
|
||||
|
||||
|
||||
### TESTS
|
||||
@@ -96,8 +97,6 @@ tests = [
|
||||
['test_strutil', ['tests/test_strutil.c', 'src/strutil.c']],
|
||||
]
|
||||
|
||||
src_dir = include_directories('src')
|
||||
|
||||
foreach t : tests
|
||||
exe = executable(t[0], t[1], include_directories: src_dir, dependencies: dependencies)
|
||||
test(t[0], exe)
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
static const char *adb_command;
|
||||
|
||||
static inline const char *get_adb_command() {
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
|
||||
// <https://stackoverflow.com/a/44383330/1987178>
|
||||
#ifdef _WIN32
|
||||
# define PRIexitcode "lu"
|
||||
# ifdef _WIN64
|
||||
# define PRIsizet PRIu64
|
||||
# define PRIexitcode "lu"
|
||||
# else
|
||||
# define PRIsizet PRIu32
|
||||
# define PRIexitcode "u"
|
||||
# endif
|
||||
#else
|
||||
# define PRIsizet "zu"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <SDL2/SDL_stdinc.h>
|
||||
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
|
||||
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
|
||||
|
||||
|
||||
@@ -145,8 +145,8 @@ SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
|
||||
|
||||
to->mouse_event.buttons = convert_mouse_buttons(SDL_BUTTON(from->button));
|
||||
to->mouse_event.position.screen_size = screen_size;
|
||||
to->mouse_event.position.point.x = (Uint16) from->x;
|
||||
to->mouse_event.position.point.y = (Uint16) from->y;
|
||||
to->mouse_event.position.point.x = from->x;
|
||||
to->mouse_event.position.point.y = from->y;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
@@ -172,7 +172,10 @@ SDL_bool mouse_wheel_from_sdl_to_android(const SDL_MouseWheelEvent *from,
|
||||
to->scroll_event.position = position;
|
||||
|
||||
int mul = from->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
|
||||
to->scroll_event.hscroll = mul * from->x;
|
||||
// SDL behavior seems inconsistent between horizontal and vertical scrolling
|
||||
// so reverse the horizontal
|
||||
// <https://wiki.libsdl.org/SDL_MouseWheelEvent#Remarks>
|
||||
to->scroll_event.hscroll = -mul * from->x;
|
||||
to->scroll_event.vscroll = mul * from->y;
|
||||
|
||||
return SDL_TRUE;
|
||||
|
||||
@@ -91,7 +91,7 @@ static int run_decoder(void *data) {
|
||||
packet.data = NULL;
|
||||
packet.size = 0;
|
||||
|
||||
while (!av_read_frame(format_ctx, &packet) && !avio_ctx->eof_reached) {
|
||||
while (!av_read_frame(format_ctx, &packet)) {
|
||||
// the new decoding/encoding API has been introduced by:
|
||||
// <http://git.videolan.org/?p=ffmpeg.git;a=commitdiff;h=7fc329e2dd6226dfecaa4a1d7adf353bf2773726>
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0)
|
||||
@@ -125,6 +125,10 @@ static int run_decoder(void *data) {
|
||||
}
|
||||
#endif
|
||||
av_packet_unref(&packet);
|
||||
|
||||
if (avio_ctx->eof_reached) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOGD("End of frames");
|
||||
|
||||
@@ -78,6 +78,10 @@ static inline void action_volume_down(struct controller *controller) {
|
||||
send_keycode(controller, AKEYCODE_VOLUME_DOWN, "VOLUME_DOWN");
|
||||
}
|
||||
|
||||
static inline void action_menu(struct controller *controller) {
|
||||
send_keycode(controller, AKEYCODE_MENU, "MENU");
|
||||
}
|
||||
|
||||
// turn the screen on if it was off, press BACK otherwise
|
||||
static void press_back_or_turn_screen_on(struct controller *controller) {
|
||||
struct control_event control_event;
|
||||
@@ -176,9 +180,12 @@ void input_manager_process_key(struct input_manager *input_manager,
|
||||
case SDLK_BACKSPACE:
|
||||
action_back(input_manager->controller);
|
||||
return;
|
||||
case SDLK_m:
|
||||
case SDLK_s:
|
||||
action_app_switch(input_manager->controller);
|
||||
return;
|
||||
case SDLK_m:
|
||||
action_menu(input_manager->controller);
|
||||
return;
|
||||
case SDLK_p:
|
||||
action_power(input_manager->controller);
|
||||
return;
|
||||
@@ -235,6 +242,17 @@ void input_manager_process_mouse_button(struct input_manager *input_manager,
|
||||
action_home(input_manager->controller);
|
||||
return;
|
||||
}
|
||||
// double-click on black borders resize to fit the device screen
|
||||
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
||||
SDL_bool outside_device_screen =
|
||||
event->x < 0 || event->x >= input_manager->screen->frame_size.width ||
|
||||
event->y < 0 || event->y >= input_manager->screen->frame_size.height;
|
||||
if (outside_device_screen) {
|
||||
screen_resize_to_fit(input_manager->screen);
|
||||
return;
|
||||
}
|
||||
// otherwise, send the click event to the device
|
||||
}
|
||||
};
|
||||
struct control_event control_event;
|
||||
if (mouse_button_from_sdl_to_android(event, input_manager->screen->frame_size, &control_event)) {
|
||||
@@ -253,7 +271,7 @@ void input_manager_process_mouse_wheel(struct input_manager *input_manager,
|
||||
struct control_event control_event;
|
||||
if (mouse_wheel_from_sdl_to_android(event, position, &control_event)) {
|
||||
if (!controller_push_event(input_manager->controller, &control_event)) {
|
||||
LOGW("Cannot send wheel button event");
|
||||
LOGW("Cannot send mouse wheel event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ struct args {
|
||||
const char *serial;
|
||||
SDL_bool help;
|
||||
SDL_bool version;
|
||||
SDL_bool show_touches;
|
||||
Uint16 port;
|
||||
Uint16 max_size;
|
||||
Uint32 bit_rate;
|
||||
@@ -45,6 +46,10 @@ static void usage(const char *arg0) {
|
||||
" The device serial number. Mandatory only if several devices\n"
|
||||
" are connected to adb.\n"
|
||||
"\n"
|
||||
" -t, --show-touches\n"
|
||||
" Enable \"show touches\" on start, disable on quit.\n"
|
||||
" It only shows physical touches (not clicks from scrcpy).\n"
|
||||
"\n"
|
||||
" -v, --version\n"
|
||||
" Print the version of scrcpy.\n"
|
||||
"\n"
|
||||
@@ -57,6 +62,7 @@ static void usage(const char *arg0) {
|
||||
" resize window to 1:1 (pixel-perfect)\n"
|
||||
"\n"
|
||||
" Ctrl+x\n"
|
||||
" Double-click on black borders\n"
|
||||
" resize window to remove black borders\n"
|
||||
"\n"
|
||||
" Ctrl+h\n"
|
||||
@@ -69,9 +75,12 @@ static void usage(const char *arg0) {
|
||||
" Right-click (when screen is on)\n"
|
||||
" click on BACK\n"
|
||||
"\n"
|
||||
" Ctrl+m\n"
|
||||
" Ctrl+s\n"
|
||||
" click on APP_SWITCH\n"
|
||||
"\n"
|
||||
" Ctrl+m\n"
|
||||
" click on MENU\n"
|
||||
"\n"
|
||||
" Ctrl+'+'\n"
|
||||
" click on VOLUME_UP\n"
|
||||
"\n"
|
||||
@@ -179,47 +188,45 @@ static SDL_bool parse_port(char *optarg, Uint16 *port) {
|
||||
|
||||
static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
||||
static const struct option long_options[] = {
|
||||
{"bit-rate", required_argument, NULL, 'b'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"max-size", required_argument, NULL, 'm'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"serial", required_argument, NULL, 's'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0 },
|
||||
{"bit-rate", required_argument, NULL, 'b'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"max-size", required_argument, NULL, 'm'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"serial", required_argument, NULL, 's'},
|
||||
{"show-touches", no_argument, NULL, 't'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0 },
|
||||
};
|
||||
int c;
|
||||
while ((c = getopt_long(argc, argv, "b:hm:p:s:v", long_options, NULL)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "b:hm:p:s:tv", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'b': {
|
||||
case 'b':
|
||||
if (!parse_bit_rate(optarg, &args->bit_rate)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
case 'h':
|
||||
args->help = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
case 'm':
|
||||
if (!parse_max_size(optarg, &args->max_size)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
case 'p':
|
||||
if (!parse_port(optarg, &args->port)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
case 's':
|
||||
args->serial = optarg;
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
case 't':
|
||||
args->show_touches = SDL_TRUE;
|
||||
break;
|
||||
case 'v':
|
||||
args->version = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// getopt prints the error message on stderr
|
||||
return SDL_FALSE;
|
||||
@@ -235,10 +242,17 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
#ifdef __WINDOWS__
|
||||
// disable buffering, we want logs immediately
|
||||
// even line buffering (setvbuf() with mode _IOLBF) is not sufficient
|
||||
setbuf(stdout, NULL);
|
||||
setbuf(stderr, NULL);
|
||||
#endif
|
||||
struct args args = {
|
||||
.serial = NULL,
|
||||
.help = SDL_FALSE,
|
||||
.version = SDL_FALSE,
|
||||
.show_touches = SDL_FALSE,
|
||||
.port = DEFAULT_LOCAL_PORT,
|
||||
.max_size = DEFAULT_MAX_SIZE,
|
||||
.bit_rate = DEFAULT_BIT_RATE,
|
||||
@@ -267,7 +281,14 @@ int main(int argc, char *argv[]) {
|
||||
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
|
||||
#endif
|
||||
|
||||
int res = scrcpy(args.serial, args.port, args.max_size, args.bit_rate) ? 0 : 1;
|
||||
struct scrcpy_options options = {
|
||||
.serial = args.serial,
|
||||
.port = args.port,
|
||||
.max_size = args.max_size,
|
||||
.bit_rate = args.bit_rate,
|
||||
.show_touches = args.show_touches,
|
||||
};
|
||||
int res = scrcpy(&options) ? 0 : 1;
|
||||
|
||||
avformat_network_deinit(); // ignore failure
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#define SHUT_RD SD_RECEIVE
|
||||
#define SHUT_WR SD_SEND
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
typedef SIZE_T size_t;
|
||||
typedef SSIZE_T ssize_t;
|
||||
typedef SOCKET socket_t;
|
||||
#else
|
||||
# include <sys/socket.h>
|
||||
|
||||
@@ -35,7 +35,29 @@ static struct input_manager input_manager = {
|
||||
.screen = &screen,
|
||||
};
|
||||
|
||||
#if defined(__APPLE__) || defined(__WINDOWS__)
|
||||
# define CONTINUOUS_RESIZING_WORKAROUND
|
||||
#endif
|
||||
|
||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
|
||||
// not triggered. As a workaround, handle them in an event handler.
|
||||
//
|
||||
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
|
||||
// <https://stackoverflow.com/a/40693139/1987178>
|
||||
static int event_watcher(void *data, SDL_Event *event) {
|
||||
if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
// called from another thread, not very safe, but it's a workaround!
|
||||
screen_render(&screen);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void event_loop(void) {
|
||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||
SDL_AddEventWatch(event_watcher, NULL);
|
||||
#endif
|
||||
SDL_Event event;
|
||||
while (SDL_WaitEvent(&event)) {
|
||||
switch (event.type) {
|
||||
@@ -57,16 +79,15 @@ static void event_loop(void) {
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
screen_render(&screen);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
screen_render(&screen);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_TEXTINPUT: {
|
||||
case SDL_TEXTINPUT:
|
||||
input_manager_process_text_input(&input_manager, &event.text);
|
||||
break;
|
||||
}
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
input_manager_process_key(&input_manager, &event.key);
|
||||
@@ -74,36 +95,56 @@ static void event_loop(void) {
|
||||
case SDL_MOUSEMOTION:
|
||||
input_manager_process_mouse_motion(&input_manager, &event.motion);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL: {
|
||||
case SDL_MOUSEWHEEL:
|
||||
input_manager_process_mouse_wheel(&input_manager, &event.wheel);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
input_manager_process_mouse_button(&input_manager, &event.button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 bit_rate) {
|
||||
if (!server_start(&server, serial, local_port, max_size, bit_rate)) {
|
||||
static process_t set_show_touches_enabled(const char *serial, SDL_bool enabled) {
|
||||
const char *value = enabled ? "1" : "0";
|
||||
const char *const adb_cmd[] = {
|
||||
"shell", "settings", "put", "system", "show_touches", value
|
||||
};
|
||||
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
|
||||
}
|
||||
|
||||
static void wait_show_touches(process_t process) {
|
||||
// reap the process, ignore the result
|
||||
process_check_success(process, "show_touches");
|
||||
}
|
||||
|
||||
SDL_bool scrcpy(const struct scrcpy_options *options) {
|
||||
if (!server_start(&server, options->serial, options->port,
|
||||
options->max_size, options->bit_rate)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
process_t proc_show_touches;
|
||||
SDL_bool show_touches_waited;
|
||||
if (options->show_touches) {
|
||||
LOGI("Enable show_touches");
|
||||
proc_show_touches = set_show_touches_enabled(options->serial, SDL_TRUE);
|
||||
show_touches_waited = SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_bool ret = SDL_TRUE;
|
||||
|
||||
if (!SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1")) {
|
||||
LOGW("Cannot request to keep default signal handlers");
|
||||
}
|
||||
|
||||
if (!sdl_init_and_configure()) {
|
||||
ret = SDL_FALSE;
|
||||
goto finally_destroy_server;
|
||||
}
|
||||
|
||||
// SDL initialization replace the signal handler for SIGTERM, so Ctrl+C is
|
||||
// managed by the event loop. This blocking call blocks the event loop, so
|
||||
// timeout the connection not to block indefinitely in case of SIGTERM.
|
||||
#define SERVER_CONNECT_TIMEOUT_MS 2000
|
||||
socket_t device_socket = server_connect_to(&server, SERVER_CONNECT_TIMEOUT_MS);
|
||||
socket_t device_socket = server_connect_to(&server);
|
||||
if (device_socket == INVALID_SOCKET) {
|
||||
server_stop(&server);
|
||||
ret = SDL_FALSE;
|
||||
@@ -153,10 +194,16 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 b
|
||||
goto finally_stop_and_join_controller;
|
||||
}
|
||||
|
||||
event_loop();
|
||||
if (options->show_touches) {
|
||||
wait_show_touches(proc_show_touches);
|
||||
show_touches_waited = SDL_TRUE;
|
||||
}
|
||||
|
||||
event_loop();
|
||||
LOGD("quit...");
|
||||
|
||||
screen_destroy(&screen);
|
||||
|
||||
finally_stop_and_join_controller:
|
||||
controller_stop(&controller);
|
||||
controller_join(&controller);
|
||||
@@ -170,6 +217,16 @@ finally_stop_decoder:
|
||||
finally_destroy_frames:
|
||||
frames_destroy(&frames);
|
||||
finally_destroy_server:
|
||||
if (options->show_touches) {
|
||||
if (!show_touches_waited) {
|
||||
// wait the process which enabled "show touches"
|
||||
wait_show_touches(proc_show_touches);
|
||||
}
|
||||
LOGI("Disable show_touches");
|
||||
proc_show_touches = set_show_touches_enabled(options->serial, SDL_FALSE);
|
||||
wait_show_touches(proc_show_touches);
|
||||
}
|
||||
|
||||
server_destroy(&server);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
|
||||
#include <SDL2/SDL_stdinc.h>
|
||||
|
||||
SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 bit_rate);
|
||||
struct scrcpy_options {
|
||||
const char *serial;
|
||||
Uint16 port;
|
||||
Uint16 max_size;
|
||||
Uint32 bit_rate;
|
||||
SDL_bool show_touches;
|
||||
};
|
||||
|
||||
SDL_bool scrcpy(const struct scrcpy_options *options);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -136,6 +136,11 @@ void screen_init(struct screen *screen) {
|
||||
*screen = (struct screen) SCREEN_INITIALIZER;
|
||||
}
|
||||
|
||||
static inline SDL_Texture *create_texture(SDL_Renderer *renderer, struct size frame_size) {
|
||||
return SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,
|
||||
frame_size.width, frame_size.height);
|
||||
}
|
||||
|
||||
SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, struct size frame_size) {
|
||||
screen->frame_size = frame_size;
|
||||
|
||||
@@ -174,8 +179,7 @@ SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, s
|
||||
SDL_FreeSurface(icon);
|
||||
|
||||
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, frame_size.width, frame_size.height);
|
||||
screen->texture = SDL_CreateTexture(screen->renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,
|
||||
frame_size.width, frame_size.height);
|
||||
screen->texture = create_texture(screen->renderer, frame_size);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
@@ -224,8 +228,7 @@ static SDL_bool prepare_for_frame(struct screen *screen, struct size new_frame_s
|
||||
|
||||
LOGD("New texture: %" PRIu16 "x%" PRIu16,
|
||||
screen->frame_size.width, screen->frame_size.height);
|
||||
screen->texture = SDL_CreateTexture(screen->renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,
|
||||
new_frame_size.width, new_frame_size.height);
|
||||
screen->texture = create_texture(screen->renderer, new_frame_size);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
return SDL_FALSE;
|
||||
|
||||
@@ -195,11 +195,11 @@ SDL_bool server_start(struct server *server, const char *serial, Uint16 local_po
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
socket_t server_connect_to(struct server *server, Uint32 timeout_ms) {
|
||||
socket_t server_connect_to(struct server *server) {
|
||||
if (!server->tunnel_forward) {
|
||||
server->device_socket = net_accept(server->server_socket);
|
||||
} else {
|
||||
Uint32 attempts = 10;
|
||||
Uint32 attempts = 50;
|
||||
Uint32 delay = 100; // ms
|
||||
server->device_socket = connect_to_server(server->local_port, attempts, delay);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ SDL_bool server_start(struct server *server, const char *serial, Uint16 local_po
|
||||
Uint16 max_size, Uint32 bit_rate);
|
||||
|
||||
// block until the communication with the server is established
|
||||
socket_t server_connect_to(struct server *server, Uint32 timeout_ms);
|
||||
socket_t server_connect_to(struct server *server);
|
||||
|
||||
// disconnect and kill the server process
|
||||
void server_stop(struct server *server);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../../command.h"
|
||||
#include "command.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../../net.h"
|
||||
#include "net.h"
|
||||
|
||||
# include <unistd.h>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../../command.h"
|
||||
#include "command.h"
|
||||
|
||||
#include "../../log.h"
|
||||
#include "../../strutil.h"
|
||||
#include "log.h"
|
||||
#include "strutil.h"
|
||||
|
||||
HANDLE cmd_execute(const char *path, const char *const argv[]) {
|
||||
STARTUPINFO si;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "../../net.h"
|
||||
#include "net.h"
|
||||
|
||||
#include "../../log.h"
|
||||
#include "log.h"
|
||||
|
||||
SDL_bool net_init(void) {
|
||||
WSADATA wsa;
|
||||
|
||||
@@ -4,10 +4,10 @@ android {
|
||||
compileSdkVersion 27
|
||||
defaultConfig {
|
||||
applicationId "com.genymobile.scrcpy"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 27
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
versionCode 2
|
||||
versionName "1.1"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
|
||||
@@ -36,7 +36,11 @@ public final class DesktopConnection implements Closeable {
|
||||
|
||||
private static LocalSocket listenAndAccept(String abstractName) throws IOException {
|
||||
LocalServerSocket localServerSocket = new LocalServerSocket(abstractName);
|
||||
return localServerSocket.accept();
|
||||
try {
|
||||
return localServerSocket.accept();
|
||||
} finally {
|
||||
localServerSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.graphics.Rect;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.view.Surface;
|
||||
|
||||
@@ -77,11 +78,17 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Android API 19 requires to call deprecated methods
|
||||
private boolean encode(MediaCodec codec, OutputStream outputStream) throws IOException {
|
||||
@SuppressWarnings("checkstyle:MagicNumber")
|
||||
byte[] buf = new byte[bitRate / 8]; // may contain up to 1 second of video
|
||||
boolean eof = false;
|
||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||
|
||||
ByteBuffer[] outputBuffers = null;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
outputBuffers = codec.getOutputBuffers();
|
||||
}
|
||||
while (!consumeRotationChange() && !eof) {
|
||||
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
||||
eof = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
|
||||
@@ -91,7 +98,12 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
break;
|
||||
}
|
||||
if (outputBufferId >= 0) {
|
||||
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
|
||||
ByteBuffer outputBuffer;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
outputBuffer = codec.getOutputBuffer(outputBufferId);
|
||||
} else {
|
||||
outputBuffer = outputBuffers[outputBufferId];
|
||||
}
|
||||
while (outputBuffer.hasRemaining()) {
|
||||
int remaining = outputBuffer.remaining();
|
||||
int len = Math.min(buf.length, remaining);
|
||||
@@ -100,6 +112,8 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
outputBuffer.get(buf, 0, len);
|
||||
outputStream.write(buf, 0, len);
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||
outputBuffers = codec.getOutputBuffers();
|
||||
}
|
||||
} finally {
|
||||
if (outputBufferId >= 0) {
|
||||
|
||||
Reference in New Issue
Block a user