mirror of
https://github.com/Genymobile/scrcpy
synced 2024-12-23 23:53:46 +00:00
Move server-related code to server.c
The file server.c already existed, but exposed a low-level API. Make it higher-level, so that scrcpy.c does not handle server details directly.
This commit is contained in:
parent
6c578b5caa
commit
28c5cc030b
@ -3,6 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL2/SDL_log.h>
|
||||
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
@ -63,3 +64,20 @@ process_t adb_push(const char *serial, const char *local, const char *remote) {
|
||||
const char *const adb_cmd[] = {"push", (char *) local, (char *) remote};
|
||||
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) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not execute \"%s\"", name);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
exit_code_t exit_code;
|
||||
if (!cmd_simple_wait(proc, &exit_code)) {
|
||||
if (exit_code != NO_EXIT_CODE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\"%s\" returned with value %" PRIexitcode, name, exit_code);
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\"%s\" exited unexpectedly", name);
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
@ -42,4 +42,8 @@ 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);
|
||||
|
||||
// convenience function to wait for a successful process execution
|
||||
// automatically log process errors with the provided process name
|
||||
SDL_bool process_check_success(process_t process, const char *name);
|
||||
|
||||
#endif
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
// contrary to SDLNet_TCP_Send and SDLNet_TCP_Recv, SDLNet_TCP_Accept is non-blocking
|
||||
// so we need to block before calling it
|
||||
TCPsocket blocking_accept(TCPsocket server_socket) {
|
||||
TCPsocket server_socket_accept(TCPsocket server_socket) {
|
||||
SDLNet_SocketSet set = SDLNet_AllocSocketSet(1);
|
||||
if (!set) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Could not allocate socket set");
|
||||
@ -19,7 +19,7 @@ TCPsocket blocking_accept(TCPsocket server_socket) {
|
||||
|
||||
// timeout is (2^32-1) milliseconds, this should be sufficient
|
||||
if (SDLNet_CheckSockets(set, -1) != 1) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not check socket: %s", SDL_GetError());
|
||||
SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not check socket");
|
||||
SDLNet_FreeSocketSet(set);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <SDL2/SDL_net.h>
|
||||
|
||||
TCPsocket blocking_accept(TCPsocket server_socket);
|
||||
// blocking accept on the server socket
|
||||
TCPsocket server_socket_accept(TCPsocket server_socket);
|
||||
|
||||
#endif
|
||||
|
100
app/src/scrcpy.c
100
app/src/scrcpy.c
@ -23,6 +23,7 @@
|
||||
|
||||
#define DEVICE_NAME_FIELD_LENGTH 64
|
||||
|
||||
static struct server server = SERVER_INITIALIZER;
|
||||
static struct screen screen = SCREEN_INITIALIZER;
|
||||
static struct frames frames;
|
||||
static struct decoder decoder;
|
||||
@ -46,14 +47,6 @@ static void count_frame(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static TCPsocket listen_on_port(Uint16 port) {
|
||||
IPaddress addr = {
|
||||
.host = INADDR_ANY,
|
||||
.port = SDL_SwapBE16(port),
|
||||
};
|
||||
return SDLNet_TCP_Open(&addr);
|
||||
}
|
||||
|
||||
// name must be at least DEVICE_NAME_FIELD_LENGTH bytes
|
||||
static SDL_bool read_initial_device_info(TCPsocket socket, char *device_name, struct size *size) {
|
||||
unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
|
||||
@ -80,23 +73,6 @@ static struct point get_mouse_point(void) {
|
||||
};
|
||||
}
|
||||
|
||||
static int wait_for_success(process_t proc, const char *name) {
|
||||
if (proc == PROCESS_NONE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not execute \"%s\"", name);
|
||||
return -1;
|
||||
}
|
||||
exit_code_t exit_code;
|
||||
if (!cmd_simple_wait(proc, &exit_code)) {
|
||||
if (exit_code != NO_EXIT_CODE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\"%s\" returned with value %" PRIexitcode, name, exit_code);
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\"%s\" exited unexpectedly", name);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void send_keycode(enum android_keycode keycode, const char *name) {
|
||||
// send DOWN event
|
||||
struct control_event control_event = {
|
||||
@ -344,43 +320,17 @@ static void event_loop(void) {
|
||||
}
|
||||
|
||||
SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 bit_rate) {
|
||||
SDL_bool ret = SDL_TRUE;
|
||||
|
||||
process_t push_proc = push_server(serial);
|
||||
if (wait_for_success(push_proc, "adb push")) {
|
||||
if (!server_start(&server, serial, local_port, max_size, bit_rate)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
process_t reverse_tunnel_proc = enable_tunnel(serial, local_port);
|
||||
if (wait_for_success(reverse_tunnel_proc, "adb reverse")) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
TCPsocket server_socket = listen_on_port(local_port);
|
||||
if (!server_socket) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not open video socket");
|
||||
goto finally_adb_reverse_remove;
|
||||
}
|
||||
|
||||
// server will connect to our socket
|
||||
process_t server = start_server(serial, max_size, bit_rate);
|
||||
if (server == PROCESS_NONE) {
|
||||
ret = SDL_FALSE;
|
||||
SDLNet_TCP_Close(server_socket);
|
||||
goto finally_adb_reverse_remove;
|
||||
}
|
||||
|
||||
// to reduce startup time, we could be tempted to init other stuff before blocking here
|
||||
// but we should not block after SDL_Init since it handles the signals (Ctrl+C) in its
|
||||
// event loop: blocking could lead to deadlock
|
||||
TCPsocket device_socket = blocking_accept(server_socket);
|
||||
// we don't need the server socket anymore
|
||||
SDLNet_TCP_Close(server_socket);
|
||||
TCPsocket device_socket = server_connect_to(&server);
|
||||
if (!device_socket) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not accept video socket: %s", SDL_GetError());
|
||||
ret = SDL_FALSE;
|
||||
stop_server(server);
|
||||
goto finally_adb_reverse_remove;
|
||||
server_stop(&server, serial);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
||||
@ -391,19 +341,17 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 b
|
||||
// to init the window immediately
|
||||
if (!read_initial_device_info(device_socket, device_name, &frame_size)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not retrieve initial screen size");
|
||||
ret = SDL_FALSE;
|
||||
SDLNet_TCP_Close(device_socket);
|
||||
stop_server(server);
|
||||
goto finally_adb_reverse_remove;
|
||||
server_stop(&server, serial);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (!frames_init(&frames)) {
|
||||
ret = SDL_FALSE;
|
||||
SDLNet_TCP_Close(device_socket);
|
||||
stop_server(server);
|
||||
goto finally_adb_reverse_remove;
|
||||
server_stop(&server, serial);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_bool ret = SDL_TRUE;
|
||||
|
||||
decoder.frames = &frames;
|
||||
decoder.video_socket = device_socket;
|
||||
|
||||
@ -411,22 +359,17 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 b
|
||||
// start the decoder
|
||||
if (!decoder_start(&decoder)) {
|
||||
ret = SDL_FALSE;
|
||||
SDLNet_TCP_Close(device_socket);
|
||||
stop_server(server);
|
||||
server_stop(&server, serial);
|
||||
goto finally_destroy_frames;
|
||||
}
|
||||
|
||||
if (!controller_init(&controller, device_socket)) {
|
||||
ret = SDL_FALSE;
|
||||
SDLNet_TCP_Close(device_socket);
|
||||
stop_server(server);
|
||||
goto finally_stop_decoder;
|
||||
}
|
||||
|
||||
if (!controller_start(&controller)) {
|
||||
ret = SDL_FALSE;
|
||||
SDLNet_TCP_Close(device_socket);
|
||||
stop_server(server);
|
||||
goto finally_destroy_controller;
|
||||
}
|
||||
|
||||
@ -450,28 +393,11 @@ finally_stop_and_join_controller:
|
||||
finally_destroy_controller:
|
||||
controller_destroy(&controller);
|
||||
finally_stop_decoder:
|
||||
SDLNet_TCP_Close(device_socket);
|
||||
|
||||
// let the server some time to print any exception trace before killing it
|
||||
struct timespec timespec = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 100000000, // 100ms
|
||||
};
|
||||
nanosleep(×pec, NULL); // ignore error
|
||||
|
||||
// kill the server before decoder_join() to wake up the decoder
|
||||
stop_server(server);
|
||||
server_stop(&server, serial);
|
||||
decoder_join(&decoder);
|
||||
finally_destroy_frames:
|
||||
frames_destroy(&frames);
|
||||
finally_adb_reverse_remove:
|
||||
{
|
||||
process_t remove = disable_tunnel(serial);
|
||||
if (remove != PROCESS_NONE) {
|
||||
// ignore failure
|
||||
cmd_simple_wait(remove, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
105
app/src/server.c
105
app/src/server.c
@ -1,28 +1,34 @@
|
||||
#include "command.h"
|
||||
#include "server.h"
|
||||
|
||||
#include <SDL2/SDL_log.h>
|
||||
#include <SDL2/SDL_net.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include "netutil.h"
|
||||
|
||||
#define SOCKET_NAME "scrcpy"
|
||||
|
||||
process_t push_server(const char *serial) {
|
||||
static SDL_bool push_server(const char *serial) {
|
||||
const char *server_path = getenv("SCRCPY_SERVER_JAR");
|
||||
if (!server_path) {
|
||||
server_path = "scrcpy-server.jar";
|
||||
}
|
||||
return adb_push(serial, server_path, "/data/local/tmp/scrcpy-server.jar");
|
||||
process_t process = adb_push(serial, server_path, "/data/local/tmp/scrcpy-server.jar");
|
||||
return process_check_success(process, "adb push");
|
||||
}
|
||||
|
||||
process_t enable_tunnel(const char *serial, Uint16 local_port) {
|
||||
return adb_reverse(serial, SOCKET_NAME, local_port);
|
||||
static SDL_bool enable_tunnel(const char *serial, Uint16 local_port) {
|
||||
process_t process = adb_reverse(serial, SOCKET_NAME, local_port);
|
||||
return process_check_success(process, "adb reverse");
|
||||
}
|
||||
|
||||
process_t disable_tunnel(const char *serial) {
|
||||
return adb_reverse_remove(serial, SOCKET_NAME);
|
||||
static SDL_bool disable_tunnel(const char *serial) {
|
||||
process_t process = adb_reverse_remove(serial, SOCKET_NAME);
|
||||
return process_check_success(process, "adb reverse --remove");
|
||||
}
|
||||
|
||||
process_t start_server(const char *serial, Uint16 max_size, Uint32 bit_rate) {
|
||||
static process_t execute_server(const char *serial, Uint16 max_size, Uint32 bit_rate) {
|
||||
char max_size_string[6];
|
||||
char bit_rate_string[11];
|
||||
sprintf(max_size_string, "%"PRIu16, max_size);
|
||||
@ -39,8 +45,89 @@ process_t start_server(const char *serial, Uint16 max_size, Uint32 bit_rate) {
|
||||
return adb_execute(serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
|
||||
}
|
||||
|
||||
void stop_server(process_t server) {
|
||||
static void terminate_server(process_t server) {
|
||||
if (!cmd_terminate(server)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not terminate server: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static TCPsocket listen_on_port(Uint16 port) {
|
||||
IPaddress addr = {
|
||||
.host = INADDR_ANY,
|
||||
.port = SDL_SwapBE16(port),
|
||||
};
|
||||
return SDLNet_TCP_Open(&addr);
|
||||
}
|
||||
|
||||
void server_init(struct server *server) {
|
||||
*server = (struct server) SERVER_INITIALIZER;
|
||||
}
|
||||
|
||||
SDL_bool server_start(struct server *server, const char *serial, Uint16 local_port,
|
||||
Uint16 max_size, Uint32 bit_rate) {
|
||||
if (!push_server(serial)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (!enable_tunnel(serial, local_port)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
// At the application level, the device part is "the server" because it
|
||||
// serves video stream and control. However, at network level, the client
|
||||
// listens and the server connects to the client. That way, the client can
|
||||
// listen before starting the server app, so there is no need to try to
|
||||
// connect until the server socket is listening on the device.
|
||||
|
||||
server->server_socket = listen_on_port(local_port);
|
||||
if (!server->server_socket) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not listen on port %" PRIu16, local_port);
|
||||
disable_tunnel(serial);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
// server will connect to our server socket
|
||||
server->process = execute_server(serial, max_size, bit_rate);
|
||||
if (server->process == PROCESS_NONE) {
|
||||
SDLNet_TCP_Close(server->server_socket);
|
||||
disable_tunnel(serial);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
server->adb_reverse_enabled = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
TCPsocket server_connect_to(struct server *server) {
|
||||
SDL_assert(server->server_socket);
|
||||
server->device_socket = server_socket_accept(server->server_socket);
|
||||
// we don't need the server socket anymore
|
||||
SDLNet_TCP_Close(server->server_socket);
|
||||
server->server_socket = NULL;
|
||||
return server->device_socket;
|
||||
}
|
||||
|
||||
void server_stop(struct server *server, const char *serial) {
|
||||
SDL_assert(server->process != PROCESS_NONE);
|
||||
if (server->server_socket) {
|
||||
SDLNet_TCP_Close(server->server_socket);
|
||||
}
|
||||
if (server->device_socket) {
|
||||
SDLNet_TCP_Close(server->device_socket);
|
||||
}
|
||||
|
||||
// let the server some time to print any exception trace before killing it
|
||||
struct timespec timespec = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 100000000, // 100ms
|
||||
};
|
||||
nanosleep(×pec, NULL); // ignore error
|
||||
|
||||
terminate_server(server->process);
|
||||
|
||||
if (server->adb_reverse_enabled) {
|
||||
// ignore failure
|
||||
disable_tunnel(serial);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,34 @@
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <SDL2/SDL_net.h>
|
||||
#include "command.h"
|
||||
|
||||
process_t push_server(const char *serial);
|
||||
process_t enable_tunnel(const char *serial, Uint16 local_port);
|
||||
process_t disable_tunnel(const char *serial);
|
||||
struct server {
|
||||
process_t process;
|
||||
TCPsocket server_socket;
|
||||
TCPsocket device_socket;
|
||||
SDL_bool adb_reverse_enabled;
|
||||
};
|
||||
|
||||
process_t start_server(const char *serial, Uint16 max_size, Uint32 bit_rate);
|
||||
void stop_server(process_t server);
|
||||
#define SERVER_INITIALIZER { \
|
||||
.process = PROCESS_NONE, \
|
||||
.server_socket = NULL, \
|
||||
.device_socket = NULL, \
|
||||
.adb_reverse_enabled = SDL_FALSE, \
|
||||
}
|
||||
|
||||
// init default values
|
||||
void server_init(struct server *server);
|
||||
|
||||
// push, enable tunnel et start the server
|
||||
SDL_bool server_start(struct server *server, const char *serial, Uint16 local_port,
|
||||
Uint16 max_size, Uint32 bit_rate);
|
||||
|
||||
// block until the communication with the server is established
|
||||
TCPsocket server_connect_to(struct server *server);
|
||||
|
||||
// disconnect and kill the server process
|
||||
void server_stop(struct server *server, const char *serial);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user