Compare commits

...

23 Commits

Author SHA1 Message Date
57687bdfcd Write header file with correct extradata
When recording, the header must be written with extradata set to the
content of the very first packet.

Suggested-by: Steve Lhomme <robux4@ycbcr.xyz>
2019-02-09 12:54:12 +01:00
ee3cba57a8 Forward FFmpeg logs
FFmpeg logs can be useful to understand the cause of issues.
2019-02-09 12:27:13 +01:00
c11905b860 Add log verbose macro
This was the only log priority missing.
2019-02-09 12:27:13 +01:00
1a5ba59504 Fix memory leak on close
The buffer associated to the AVIOContext must be freed.
2019-02-09 12:27:13 +01:00
aa07dfd2ca Merge pull request #412 from npes87184/dev
app: add always_on_top
2019-01-27 14:09:19 +01:00
eca82e09c3 app: add always_on_top
It is very convenient when I play mobile game and watch video at the
same time.

Tested on Linux mint Cinnamon as well as Windows 10.

Signed-off-by: Yu-Chen Lin <npes87184@gmail.com>
2019-01-27 20:54:24 +08:00
b35733edb6 Fix expected mouse event sizes
Commit fefb9816a9 modified mouse events
serialization. The server-side parsing was updated to correctly read the
position, but the expected size of these events was not updated.

As a result, the server might try to parse incomplete events, leading
to BufferUnderflowException.

Fixes
<https://github.com/Genymobile/scrcpy/issues/350#issuecomment-456298816>.
2019-01-22 09:47:26 +01:00
7764a836f1 Fix incorrect comment
Comment had not been updated along with the code.
2019-01-22 09:38:51 +01:00
0bfaf7b7ff Update links to v1.6 in README and BUILD 2019-01-20 22:05:32 +01:00
446c682374 Bump version to 1.6 2019-01-20 21:35:42 +01:00
3aacb7967d Merge branch 'dev' into master 2019-01-19 21:40:55 +01:00
06a0bbbc71 Update FFmpeg (4.1) for Windows
Include the last version of FFmpeg in Windows releases.
2019-01-19 18:14:05 +01:00
d71e036f3a Do not disable screensaver
Keep screensaver enabled while scrcpy is running.

Fixes <https://github.com/Genymobile/scrcpy/issues/380>.
2019-01-18 23:17:31 +01:00
b343a5dc59 Merge pull request #401 from npes87184/dev
input_manager: don't ignore double click event when clicking inside device
2019-01-18 13:34:41 +01:00
c5ec1a194c input_manager: don't ignore double click event when clicking inside device
Signed-off-by: Yu-Chen Lin <npes87184@gmail.com>
2019-01-18 20:21:25 +08:00
4f254d8232 Merge pull request #396 from npes87184/master
prepare-dep: use variable for better readability
2019-01-16 21:16:04 +01:00
1fdde490fd Mirror "secure" content
Some applications, like Silence, prevent the content of a window from
being viewed on non-secure displays:
<https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_SECURE>

We can mirror it by just creating a "secure" display:
<https://developer.android.com/reference/android/view/Display#FLAG_SECURE>
2019-01-16 14:21:18 +01:00
fb73aa101a prepare-dep: use variable for better readability
The arguments are saved to variable when script started. Instead of
using $1, $2 and $3, we can use these variables.

Signed-off-by: yuchenlin <npes87184@gmail.com>
2019-01-15 21:32:46 +08:00
39c5e71605 Make the server unlink itself
To clean up the device, the client executed "adb shell rm" once the
server was guaranteed to be started (after the connection succeeded).

This implied to track whether the installation state, and failed if an
additional tunnel was used in "forward" mode:
<https://github.com/Genymobile/scrcpy/issues/386#issuecomment-453936034>

Instead, make the server unlink itself on start.
2019-01-14 21:12:23 +01:00
fefb9816a9 Handle mouse events outside device screen
Mouse events position were unsigned (so negative values could not be
handled properly).

To avoid issues with negative values, mouse events outside the device
screen were ignored (commit a7fe9ad779).

But as a consequence, drag&drop were "broken" if the "drop" occurred
outside the device screen.

Instead, use signed 32-bits to store the position, and forward events
outside the device screen.

Fixes <https://github.com/Genymobile/scrcpy/issues/357>.
2018-11-27 08:59:22 +01:00
7830859c21 Merge branch 'master' into dev 2018-11-27 08:38:57 +01:00
1e22ebcac2 Always use non-empty arguments
The client passes parameters to the server via "adb shell" arguments.

Use "-" instead of "" when no crop is specified to avoid empty
arguments, which are not handled the same way on all devices.

Fixed <https://github.com/Genymobile/scrcpy/issues/337>.
2018-11-16 18:39:41 +01:00
d81729ba39 Always expect 5 parameters for the server
The client always sends all the arguments, so there is no need to check.
2018-11-16 18:36:01 +01:00
29 changed files with 229 additions and 168 deletions

View File

@ -234,10 +234,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server
- [`scrcpy-server-v1.5.jar`][direct-scrcpy-server]
_(SHA-256: d97aab6f60294e33e7ff79c2856ad3e01f912892395131f4f337e9ece03c24de)_
- [`scrcpy-server-v1.6.jar`][direct-scrcpy-server]
_(SHA-256: 08df924bf6d10943df9eaacfff548a99871ebfca4641f8c7ddddb73f27cb905b)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.5-fixversion/scrcpy-server-v1.5.jar
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.6/scrcpy-server-v1.6.jar
Download the prebuilt server somewhere, and specify its path during the Meson
configuration:

14
FAQ.md
View File

@ -24,20 +24,6 @@ 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.
[protect]: https://developer.android.com/reference/android/view/Display#FLAG_SECURE
[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].

View File

@ -100,10 +100,10 @@ dist-win32: build-server build-win32 build-win32-noconsole
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN32_TARGET_DIR)/"
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe"
cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
@ -114,10 +114,10 @@ dist-win64: build-server build-win64 build-win64-noconsole
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN64_TARGET_DIR)/"
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe"
cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"

View File

@ -1,4 +1,4 @@
# scrcpy (v1.5)
# scrcpy (v1.6)
This application provides display and control of Android devices connected on
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
@ -47,13 +47,13 @@ For Gentoo, an [Ebuild] is available: [`scrcpy/`][ebuild-link].
For Windows, for simplicity, prebuilt archives with all the dependencies
(including `adb`) are available:
- [`scrcpy-win32-v1.5.zip`][direct-win32]
_(SHA-256: 46ae0d4c1c6bd049ec4a30080d2ad91a32b31d3f758afdca2c3a915ecabf02c1)_
- [`scrcpy-win64-v1.5.zip`][direct-win64]
_(SHA-256: 89daa07325129617cf943a84bc4e304ee5e57118416fe265b9b5d4a1bf87c501)_
- [`scrcpy-win32-v1.6.zip`][direct-win32]
_(SHA-256: 4ca0c5924ab2ebf19b70f6598b2e546f65ba469a72ded2d1b213df3380fb46b1)_
- [`scrcpy-win64-v1.6.zip`][direct-win64]
_(SHA-256: f66b7eace8dd6537a9a27176fd824704a284d8e82077ccc903344396043f90c9)_
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.5-fixversion/scrcpy-win32-v1.5.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.5-fixversion/scrcpy-win64-v1.5.zip
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.6/scrcpy-win32-v1.6.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.6/scrcpy-win64-v1.6.zip
You can also [build the app manually][BUILD].
@ -196,6 +196,16 @@ scrcpy -f # short version
Fullscreen can then be toggled dynamically with `Ctrl`+`f`.
### Always on top
The window of app can always be above others by:
```bash
scrcpy --always-on-top
scrcpy -T # short version
```
### Show touches
For presentations, it may be useful to show physical touches (on the physical

View File

@ -135,11 +135,6 @@ process_t adb_install(const char *serial, const char *local) {
return proc;
}
process_t adb_remove_path(const char *serial, const char *path) {
const char *const adb_cmd[] = {"shell", "rm", path};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
}
SDL_bool process_check_success(process_t proc, const char *name) {
if (proc == PROCESS_NONE) {
LOGE("Could not execute \"%s\"", name);

View File

@ -49,7 +49,6 @@ process_t adb_reverse(const char *serial, const char *device_socket_name, uint16
process_t adb_reverse_remove(const char *serial, const char *device_socket_name);
process_t adb_push(const char *serial, const char *local, const char *remote);
process_t adb_install(const char *serial, const char *local);
process_t adb_remove_path(const char *serial, const char *path);
// convenience function to wait for a successful process execution
// automatically log process errors with the provided process name

View File

@ -13,8 +13,8 @@ struct size {
};
struct point {
Uint16 x;
Uint16 y;
Sint32 x;
Sint32 y;
};
struct position {

View File

@ -8,10 +8,10 @@
#include "log.h"
static void write_position(Uint8 *buf, const struct position *position) {
buffer_write16be(&buf[0], position->point.x);
buffer_write16be(&buf[2], position->point.y);
buffer_write16be(&buf[4], position->screen_size.width);
buffer_write16be(&buf[6], position->screen_size.height);
buffer_write32be(&buf[0], position->point.x);
buffer_write32be(&buf[4], position->point.y);
buffer_write16be(&buf[8], position->screen_size.width);
buffer_write16be(&buf[10], position->screen_size.height);
}
int control_event_serialize(const struct control_event *event, unsigned char *buf) {
@ -23,7 +23,7 @@ int control_event_serialize(const struct control_event *event, unsigned char *bu
buffer_write32be(&buf[6], event->keycode_event.metastate);
return 10;
case CONTROL_EVENT_TYPE_TEXT: {
// write length (1 byte) + date (non nul-terminated)
// write length (2 bytes) + string (non nul-terminated)
size_t len = strlen(event->text_event.text);
if (len > TEXT_MAX_LENGTH) {
// injecting a text takes time, so limit the text length
@ -37,12 +37,12 @@ int control_event_serialize(const struct control_event *event, unsigned char *bu
buf[1] = event->mouse_event.action;
buffer_write32be(&buf[2], event->mouse_event.buttons);
write_position(&buf[6], &event->mouse_event.position);
return 14;
return 18;
case CONTROL_EVENT_TYPE_SCROLL:
write_position(&buf[1], &event->scroll_event.position);
buffer_write32be(&buf[9], (Uint32) event->scroll_event.hscroll);
buffer_write32be(&buf[13], (Uint32) event->scroll_event.vscroll);
return 17;
buffer_write32be(&buf[13], (Uint32) event->scroll_event.hscroll);
buffer_write32be(&buf[17], (Uint32) event->scroll_event.vscroll);
return 21;
case CONTROL_EVENT_TYPE_COMMAND:
buf[1] = event->command_event.action;
return 2;

View File

@ -284,7 +284,8 @@ run_quit:
run_finally_close_input:
avformat_close_input(&format_ctx);
run_finally_free_avio_ctx:
av_freep(&avio_ctx);
av_free(avio_ctx->buffer);
av_free(avio_ctx);
run_finally_free_format_ctx:
avformat_free_context(format_ctx);
run_finally_close_codec:

View File

@ -18,9 +18,15 @@ static void convert_to_renderer_coordinates(SDL_Renderer *renderer, int *x, int
*y = (int) (*y / scale_y) - viewport.y;
}
static void get_mouse_point(struct screen *screen, int *x, int *y) {
SDL_GetMouseState(x, y);
convert_to_renderer_coordinates(screen->renderer, x, y);
static struct point get_mouse_point(struct screen *screen) {
int x;
int y;
SDL_GetMouseState(&x, &y);
convert_to_renderer_coordinates(screen->renderer, &x, &y);
return (struct point) {
.x = x,
.y = y,
};
}
static const int ACTION_DOWN = 1;
@ -273,9 +279,6 @@ static SDL_bool is_outside_device_screen(struct input_manager *input_manager,
void input_manager_process_mouse_button(struct input_manager *input_manager,
const SDL_MouseButtonEvent *event) {
SDL_bool outside_device_screen = is_outside_device_screen(input_manager,
event->x,
event->y);
if (event->type == SDL_MOUSEBUTTONDOWN) {
if (event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(input_manager->controller);
@ -286,17 +289,16 @@ void input_manager_process_mouse_button(struct input_manager *input_manager,
return;
}
// double-click on black borders resize to fit the device screen
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2
&& outside_device_screen) {
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
SDL_bool outside= is_outside_device_screen(input_manager,
event->x,
event->y);
if (outside) {
screen_resize_to_fit(input_manager->screen);
return;
}
// otherwise, send the click event to the device
}
if (outside_device_screen) {
// ignore
return;
// otherwise, send the click event to the device
}
struct control_event control_event;
@ -309,22 +311,9 @@ void input_manager_process_mouse_button(struct input_manager *input_manager,
void input_manager_process_mouse_wheel(struct input_manager *input_manager,
const SDL_MouseWheelEvent *event) {
int x;
int y;
get_mouse_point(input_manager->screen, &x, &y);
if (is_outside_device_screen(input_manager, x, y)) {
// ignore
return;
}
SDL_assert_release(x >= 0 && x < 0x10000 && y >= 0 && y < 0x10000);
struct position position = {
.screen_size = input_manager->screen->frame_size,
.point = {
.x = (Uint16) x,
.y = (Uint16) y,
},
.point = get_mouse_point(input_manager->screen),
};
struct control_event control_event;
if (mouse_wheel_from_sdl_to_android(event, position, &control_event)) {

View File

@ -3,6 +3,7 @@
#include <SDL2/SDL_log.h>
#define LOGV(...) SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
#define LOGD(...) SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
#define LOGI(...) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
#define LOGW(...) SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)

View File

@ -19,6 +19,7 @@ struct args {
Uint16 port;
Uint16 max_size;
Uint32 bit_rate;
SDL_bool always_on_top;
};
static void usage(const char *arg0) {
@ -65,6 +66,9 @@ static void usage(const char *arg0) {
" Enable \"show touches\" on start, disable on quit.\n"
" It only shows physical touches (not clicks from scrcpy).\n"
"\n"
" -T, --always-on-top\n"
" Make scrcpy window always on top (above other windows).\n"
"\n"
" -v, --version\n"
" Print the version of scrcpy.\n"
"\n"
@ -206,6 +210,7 @@ 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[] = {
{"always-on-top", no_argument, NULL, 'T'},
{"bit-rate", required_argument, NULL, 'b'},
{"crop", required_argument, NULL, 'c'},
{"fullscreen", no_argument, NULL, 'f'},
@ -219,7 +224,7 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
{NULL, 0, NULL, 0 },
};
int c;
while ((c = getopt_long(argc, argv, "b:c:fhm:p:r:s:tv", long_options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "b:c:fhm:p:r:s:tTv", long_options, NULL)) != -1) {
switch (c) {
case 'b':
if (!parse_bit_rate(optarg, &args->bit_rate)) {
@ -254,6 +259,9 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
case 't':
args->show_touches = SDL_TRUE;
break;
case 'T':
args->always_on_top = SDL_TRUE;
break;
case 'v':
args->version = SDL_TRUE;
break;
@ -288,6 +296,7 @@ int main(int argc, char *argv[]) {
.port = DEFAULT_LOCAL_PORT,
.max_size = DEFAULT_MAX_SIZE,
.bit_rate = DEFAULT_BIT_RATE,
.always_on_top = SDL_FALSE,
};
if (!parse_args(&args, argc, argv)) {
return 1;
@ -324,6 +333,7 @@ int main(int argc, char *argv[]) {
.bit_rate = args.bit_rate,
.show_touches = args.show_touches,
.fullscreen = args.fullscreen,
.always_on_top = args.always_on_top,
};
int res = scrcpy(&options) ? 0 : 1;

View File

@ -5,6 +5,16 @@
#include "config.h"
#include "log.h"
// In ffmpeg/doc/APIchanges:
// 2016-04-11 - 6f69f7a / 9200514 - lavf 57.33.100 / 57.5.0 - avformat.h
// Add AVStream.codecpar, deprecate AVStream.codec.
#if (LIBAVFORMAT_VERSION_MICRO >= 100 /* FFmpeg */ && \
LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) \
|| (LIBAVFORMAT_VERSION_MICRO < 100 && /* Libav */ \
LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 5, 0))
# define LAVF_NEW_CODEC_API
#endif
static const AVOutputFormat *find_mp4_muxer(void) {
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 9, 100)
void *opaque = NULL;
@ -30,6 +40,7 @@ SDL_bool recorder_init(struct recorder *recorder, const char *filename,
}
recorder->declared_frame_size = declared_frame_size;
recorder->header_written = SDL_FALSE;
return SDL_TRUE;
}
@ -63,13 +74,7 @@ SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec) {
return SDL_FALSE;
}
// In ffmpeg/doc/APIchanges:
// 2016-04-11 - 6f69f7a / 9200514 - lavf 57.33.100 / 57.5.0 - avformat.h
// Add AVStream.codecpar, deprecate AVStream.codec.
#if (LIBAVFORMAT_VERSION_MICRO >= 100 /* FFmpeg */ && \
LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) \
|| (LIBAVFORMAT_VERSION_MICRO < 100 && /* Libav */ \
LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 5, 0))
#ifdef LAVF_NEW_CODEC_API
ostream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
ostream->codecpar->codec_id = input_codec->id;
ostream->codecpar->format = AV_PIX_FMT_YUV420P;
@ -93,14 +98,6 @@ SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec) {
return SDL_FALSE;
}
ret = avformat_write_header(recorder->ctx, NULL);
if (ret < 0) {
LOGE("Failed to write header to %s", recorder->filename);
avio_closep(&recorder->ctx->pb);
avformat_free_context(recorder->ctx);
return SDL_FALSE;
}
return SDL_TRUE;
}
@ -113,6 +110,46 @@ void recorder_close(struct recorder *recorder) {
avformat_free_context(recorder->ctx);
}
SDL_bool recorder_write_header(struct recorder *recorder, AVPacket *packet) {
AVStream *ostream = recorder->ctx->streams[0];
uint8_t *extradata = SDL_malloc(packet->size * sizeof(uint8_t));
if (!extradata) {
LOGC("Cannot allocate extradata");
return SDL_FALSE;
}
// copy the first packet to the extra data
memcpy(extradata, packet->data, packet->size);
#ifdef LAVF_NEW_CODEC_API
ostream->codecpar->extradata = extradata;
ostream->codecpar->extradata_size = packet->size;
#else
ostream->codec->extradata = extradata;
ostream->codec->extradata_size = packet->size;
#endif
int ret = avformat_write_header(recorder->ctx, NULL);
if (ret < 0) {
LOGE("Failed to write header to %s", recorder->filename);
SDL_free(extradata);
avio_closep(&recorder->ctx->pb);
avformat_free_context(recorder->ctx);
return SDL_FALSE;
}
return SDL_TRUE;
}
SDL_bool recorder_write(struct recorder *recorder, AVPacket *packet) {
if (!recorder->header_written) {
SDL_bool ok = recorder_write_header(recorder, packet);
if (!ok) {
return SDL_FALSE;
}
recorder->header_written = SDL_TRUE;
}
return av_write_frame(recorder->ctx, packet) >= 0;
}

View File

@ -10,6 +10,7 @@ struct recorder {
char *filename;
AVFormatContext *ctx;
struct size declared_frame_size;
SDL_bool header_written;
};
SDL_bool recorder_init(struct recorder *recoder, const char *filename,

View File

@ -139,6 +139,40 @@ static void wait_show_touches(process_t process) {
process_check_success(process, "show_touches");
}
static SDL_LogPriority sdl_priority_from_av_level(int level) {
switch (level) {
case AV_LOG_PANIC:
case AV_LOG_FATAL:
return SDL_LOG_PRIORITY_CRITICAL;
case AV_LOG_ERROR:
return SDL_LOG_PRIORITY_ERROR;
case AV_LOG_WARNING:
return SDL_LOG_PRIORITY_WARN;
case AV_LOG_INFO:
return SDL_LOG_PRIORITY_INFO;
}
// do not forward others, which are too verbose
return 0;
}
static void
av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
SDL_LogPriority priority = sdl_priority_from_av_level(level);
if (priority == 0) {
return;
}
char *local_fmt = SDL_malloc(strlen(fmt) + 10);
if (!local_fmt) {
LOGC("Cannot allocate string");
return;
}
// strcpy is safe here, the destination is large enough
strcpy(local_fmt, "[FFmpeg] ");
strcpy(local_fmt + 9, fmt);
SDL_LogMessageV(SDL_LOG_CATEGORY_VIDEO, priority, local_fmt, vl);
SDL_free(local_fmt);
}
SDL_bool scrcpy(const struct scrcpy_options *options) {
SDL_bool send_frame_meta = !!options->record_filename;
if (!server_start(&server, options->serial, options->port,
@ -203,6 +237,8 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
rec = &recorder;
}
av_log_set_callback(av_log_callback);
decoder_init(&decoder, &frames, device_socket, rec);
// now we consumed the header values, the socket receives the video stream
@ -223,7 +259,7 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
goto finally_destroy_controller;
}
if (!screen_init_rendering(&screen, device_name, frame_size)) {
if (!screen_init_rendering(&screen, device_name, frame_size, options->always_on_top)) {
ret = SDL_FALSE;
goto finally_stop_and_join_controller;
}

View File

@ -12,6 +12,7 @@ struct scrcpy_options {
Uint32 bit_rate;
SDL_bool show_touches;
SDL_bool fullscreen;
SDL_bool always_on_top;
};
SDL_bool scrcpy(const struct scrcpy_options *options);

View File

@ -30,6 +30,9 @@ SDL_bool sdl_init_and_configure(void) {
}
#endif
// Do not disable the screensaver when scrcpy is running
SDL_EnableScreenSaver();
return SDL_TRUE;
}
@ -141,7 +144,10 @@ static inline SDL_Texture *create_texture(SDL_Renderer *renderer, struct size fr
frame_size.width, frame_size.height);
}
SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, struct size frame_size) {
SDL_bool screen_init_rendering(struct screen *screen,
const char *device_name,
struct size frame_size,
SDL_bool always_on_top) {
screen->frame_size = frame_size;
struct size window_size = get_initial_optimal_size(frame_size);
@ -149,6 +155,10 @@ SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, s
#ifdef HIDPI_SUPPORT
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
#endif
if (always_on_top) {
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
}
screen->window = SDL_CreateWindow(device_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
window_size.width, window_size.height, window_flags);
if (!screen->window) {

View File

@ -43,7 +43,8 @@ void screen_init(struct screen *screen);
// initialize screen, create window, renderer and texture (window is hidden)
SDL_bool screen_init_rendering(struct screen *screen,
const char *device_name,
struct size frame_size);
struct size frame_size,
SDL_bool always_on_top);
// show the window
void screen_show_window(struct screen *screen);

View File

@ -34,11 +34,6 @@ static SDL_bool push_server(const char *serial) {
return process_check_success(process, "adb push");
}
static SDL_bool remove_server(const char *serial) {
process_t process = adb_remove_path(serial, DEVICE_SERVER_PATH);
return process_check_success(process, "adb shell rm");
}
static SDL_bool enable_tunnel_reverse(const char *serial, Uint16 local_port) {
process_t process = adb_reverse(serial, SOCKET_NAME, local_port);
return process_check_success(process, "adb reverse");
@ -93,7 +88,7 @@ static process_t execute_server(const char *serial,
max_size_string,
bit_rate_string,
tunnel_forward ? "true" : "false",
crop ? crop : "''",
crop ? crop : "-",
send_frame_meta ? "true" : "false",
};
return adb_execute(serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
@ -167,8 +162,6 @@ SDL_bool server_start(struct server *server, const char *serial,
return SDL_FALSE;
}
server->server_copied_to_device = SDL_TRUE;
if (!enable_tunnel(server)) {
SDL_free((void *) server->serial);
return SDL_FALSE;
@ -229,10 +222,6 @@ socket_t server_connect_to(struct server *server) {
close_socket(&server->server_socket);
}
// the server is started, we can clean up the jar from the temporary folder
remove_server(server->serial); // ignore failure
server->server_copied_to_device = SDL_FALSE;
// we don't need the adb tunnel anymore
disable_tunnel(server); // ignore failure
server->tunnel_enabled = SDL_FALSE;
@ -254,10 +243,6 @@ void server_stop(struct server *server) {
// ignore failure
disable_tunnel(server);
}
if (server->server_copied_to_device) {
remove_server(server->serial); // ignore failure
}
}
void server_destroy(struct server *server) {

View File

@ -13,7 +13,6 @@ struct server {
SDL_bool tunnel_enabled;
SDL_bool tunnel_forward; // use "adb forward" instead of "adb reverse"
SDL_bool send_frame_meta; // request frame PTS to be able to record properly
SDL_bool server_copied_to_device;
};
#define SERVER_INITIALIZER { \
@ -25,7 +24,6 @@ struct server {
.tunnel_enabled = SDL_FALSE, \
.tunnel_forward = SDL_FALSE, \
.send_frame_meta = SDL_FALSE, \
.server_copied_to_device = SDL_FALSE, \
}
// init default values

View File

@ -15,6 +15,6 @@ cpu = 'i686'
endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.0.2-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.0.2-win32-dev'
prebuilt_ffmpeg_shared = 'ffmpeg-4.1-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.1-win32-dev'
prebuilt_sdl2 = 'SDL2-2.0.9/i686-w64-mingw32'

View File

@ -15,6 +15,6 @@ cpu = 'x86_64'
endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.0.2-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.0.2-win64-dev'
prebuilt_ffmpeg_shared = 'ffmpeg-4.1-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.1-win64-dev'
prebuilt_sdl2 = 'SDL2-2.0.9/x86_64-w64-mingw32'

View File

@ -1,5 +1,5 @@
project('scrcpy', 'c',
version: '1.5',
version: '1.6',
meson_version: '>= 0.37',
default_options: 'c_std=c11')

View File

@ -10,24 +10,24 @@ prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
prepare-ffmpeg-shared-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0.2-win32-shared.zip \
cc190a3a4cf7bfbd4fbaa92609c1501a1de458055e6cfea8b745c1d515013aa8 \
ffmpeg-4.0.2-win32-shared
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.1-win32-shared.zip \
e692b18c01745d262c03294b382fd64df68fabe3c66aa4546a3ad3935175cde3 \
ffmpeg-4.1-win32-shared
prepare-ffmpeg-dev-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.0.2-win32-dev.zip \
c72c74bad74ac0541f1b43090c26a50017c49041c182a703abd2057bb8cdc238 \
ffmpeg-4.0.2-win32-dev
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.1-win32-dev.zip \
34bc5e471fb9160609abd6bc271e361050f3ff7376b1b8a0873cca02b38277c8 \
ffmpeg-4.1-win32-dev
prepare-ffmpeg-shared-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.0.2-win64-shared.zip \
ede566aca8b5348dff85570f9638c6bad33209f9419f79db7bde7daa37599bff \
ffmpeg-4.0.2-win64-shared
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.1-win64-shared.zip \
c4908c97436c946509dc365e421159274fa4b1e66dce6fb5b63d82a6294d5357 \
ffmpeg-4.1-win64-shared
prepare-ffmpeg-dev-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.0.2-win64-dev.zip \
23ee994161c51285cb956b98d3caa499d48083dae7b26c1fdf77f22e98df1c5f \
ffmpeg-4.0.2-win64-dev
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.1-win64-dev.zip \
761ec79aa3dae66698c9791a2f0bb9da8794246f8356cadc741ddc0eabab0471 \
ffmpeg-4.1-win64-dev
prepare-sdl2:
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.9-mingw.tar.gz \

View File

@ -55,4 +55,4 @@ get_dep() {
fi
}
get_dep "$1" "$2" "$3"
get_dep "$url" "$sum" "$dir"

View File

@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 27
versionCode 6
versionName "1.5"
versionCode 7
versionName "1.6"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {

View File

@ -9,8 +9,8 @@ import java.nio.charset.StandardCharsets;
public class ControlEventReader {
private static final int KEYCODE_PAYLOAD_LENGTH = 9;
private static final int MOUSE_PAYLOAD_LENGTH = 13;
private static final int SCROLL_PAYLOAD_LENGTH = 16;
private static final int MOUSE_PAYLOAD_LENGTH = 17;
private static final int SCROLL_PAYLOAD_LENGTH = 20;
private static final int COMMAND_PAYLOAD_LENGTH = 1;
public static final int TEXT_MAX_LENGTH = 300;
@ -132,8 +132,8 @@ public class ControlEventReader {
}
private static Position readPosition(ByteBuffer buffer) {
int x = toUnsigned(buffer.getShort());
int y = toUnsigned(buffer.getShort());
int x = buffer.getInt();
int y = buffer.getInt();
int screenWidth = toUnsigned(buffer.getShort());
int screenHeight = toUnsigned(buffer.getShort());
return new Position(x, y, screenWidth, screenHeight);

View File

@ -151,7 +151,7 @@ public class ScreenEncoder implements Device.RotationListener {
}
private static IBinder createDisplay() {
return SurfaceControl.createDisplay("scrcpy", false);
return SurfaceControl.createDisplay("scrcpy", true);
}
private static void configure(MediaCodec codec, MediaFormat format) {

View File

@ -2,11 +2,14 @@ package com.genymobile.scrcpy;
import android.graphics.Rect;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
public final class Server {
private static final String SERVER_PATH = "/data/local/tmp/scrcpy-server.jar";
private Server() {
// not instantiable
}
@ -46,35 +49,24 @@ public final class Server {
@SuppressWarnings("checkstyle:MagicNumber")
private static Options createOptions(String... args) {
if (args.length != 5)
throw new IllegalArgumentException("Expecting 5 parameters");
Options options = new Options();
if (args.length < 1) {
return options;
}
int maxSize = Integer.parseInt(args[0]) & ~7; // multiple of 8
options.setMaxSize(maxSize);
if (args.length < 2) {
return options;
}
int bitRate = Integer.parseInt(args[1]);
options.setBitRate(bitRate);
if (args.length < 3) {
return options;
}
// use "adb forward" instead of "adb tunnel"? (so the server must listen)
boolean tunnelForward = Boolean.parseBoolean(args[2]);
options.setTunnelForward(tunnelForward);
if (args.length < 4) {
return options;
}
Rect crop = parseCrop(args[3]);
options.setCrop(crop);
if (args.length < 5) {
return options;
}
boolean sendFrameMeta = Boolean.parseBoolean(args[4]);
options.setSendFrameMeta(sendFrameMeta);
@ -82,7 +74,7 @@ public final class Server {
}
private static Rect parseCrop(String crop) {
if (crop.isEmpty()) {
if ("-".equals(crop)) {
return null;
}
// input format: "width:height:x:y"
@ -97,6 +89,14 @@ public final class Server {
return new Rect(x, y, x + width, y + height);
}
private static void unlinkSelf() {
try {
new File(SERVER_PATH).delete();
} catch (Exception e) {
Ln.e("Cannot unlink server", e);
}
}
public static void main(String... args) throws Exception {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
@ -105,6 +105,7 @@ public final class Server {
}
});
unlinkSelf();
Options options = createOptions(args);
scrcpy(options);
}