Compare commits
7 Commits
master
...
twosockets
Author | SHA1 | Date | |
---|---|---|---|
|
72c83b2ac2 | ||
|
7dcac5b8b3 | ||
|
f88345de0a | ||
|
2a1bdd0af3 | ||
|
2a7fee6c16 | ||
|
92f0266704 | ||
|
6cf134f31f |
@ -143,18 +143,34 @@ else
|
|||||||
link_args = []
|
link_args = []
|
||||||
endif
|
endif
|
||||||
|
|
||||||
executable('scrcpy', src, dependencies: dependencies, include_directories: src_dir, install: true, c_args: c_args, link_args: link_args)
|
executable('scrcpy', src,
|
||||||
|
dependencies: dependencies,
|
||||||
|
include_directories: src_dir,
|
||||||
|
install: true,
|
||||||
|
c_args: c_args,
|
||||||
|
link_args: link_args)
|
||||||
|
|
||||||
|
|
||||||
### TESTS
|
### TESTS
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
['test_control_event_queue', ['tests/test_control_event_queue.c', 'src/control_event.c']],
|
['test_control_event_queue', [
|
||||||
['test_control_event_serialize', ['tests/test_control_event_serialize.c', 'src/control_event.c']],
|
'tests/test_control_event_queue.c',
|
||||||
['test_strutil', ['tests/test_strutil.c', 'src/str_util.c']],
|
'src/control_event.c'
|
||||||
|
]],
|
||||||
|
['test_control_event_serialize', [
|
||||||
|
'tests/test_control_event_serialize.c',
|
||||||
|
'src/control_event.c'
|
||||||
|
]],
|
||||||
|
['test_strutil', [
|
||||||
|
'tests/test_strutil.c',
|
||||||
|
'src/str_util.c'
|
||||||
|
]],
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach t : tests
|
foreach t : tests
|
||||||
exe = executable(t[0], t[1], include_directories: src_dir, dependencies: dependencies)
|
exe = executable(t[0], t[1],
|
||||||
|
include_directories: src_dir,
|
||||||
|
dependencies: dependencies)
|
||||||
test(t[0], exe)
|
test(t[0], exe)
|
||||||
endforeach
|
endforeach
|
||||||
|
@ -290,8 +290,7 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
goto finally_destroy_server;
|
goto finally_destroy_server;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t device_socket = server_connect_to(&server);
|
if (!server_connect_to(&server)) {
|
||||||
if (device_socket == INVALID_SOCKET) {
|
|
||||||
server_stop(&server);
|
server_stop(&server);
|
||||||
ret = false;
|
ret = false;
|
||||||
goto finally_destroy_server;
|
goto finally_destroy_server;
|
||||||
@ -303,7 +302,7 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
// screenrecord does not send frames when the screen content does not
|
// screenrecord does not send frames when the screen content does not
|
||||||
// change therefore, we transmit the screen size before the video stream,
|
// change therefore, we transmit the screen size before the video stream,
|
||||||
// to be able to init the window immediately
|
// to be able to init the window immediately
|
||||||
if (!device_read_info(device_socket, device_name, &frame_size)) {
|
if (!device_read_info(server.video_socket, device_name, &frame_size)) {
|
||||||
server_stop(&server);
|
server_stop(&server);
|
||||||
ret = false;
|
ret = false;
|
||||||
goto finally_destroy_server;
|
goto finally_destroy_server;
|
||||||
@ -342,7 +341,7 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
|
|
||||||
av_log_set_callback(av_log_callback);
|
av_log_set_callback(av_log_callback);
|
||||||
|
|
||||||
stream_init(&stream, device_socket, dec, rec);
|
stream_init(&stream, server.video_socket, dec, rec);
|
||||||
|
|
||||||
// now we consumed the header values, the socket receives the video stream
|
// now we consumed the header values, the socket receives the video stream
|
||||||
// start the stream
|
// start the stream
|
||||||
@ -354,7 +353,7 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
|
|
||||||
if (display) {
|
if (display) {
|
||||||
if (control) {
|
if (control) {
|
||||||
if (!controller_init(&controller, device_socket)) {
|
if (!controller_init(&controller, server.control_socket)) {
|
||||||
ret = false;
|
ret = false;
|
||||||
goto finally_stop_stream;
|
goto finally_stop_stream;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ connect_and_read_byte(uint16_t port) {
|
|||||||
char byte;
|
char byte;
|
||||||
// the connection may succeed even if the server behind the "adb tunnel"
|
// the connection may succeed even if the server behind the "adb tunnel"
|
||||||
// is not listening, so read one byte to detect a working connection
|
// is not listening, so read one byte to detect a working connection
|
||||||
if (net_recv_all(socket, &byte, 1) != 1) {
|
if (net_recv(socket, &byte, 1) != 1) {
|
||||||
// the server is not listening yet behind the adb tunnel
|
// the server is not listening yet behind the adb tunnel
|
||||||
return INVALID_SOCKET;
|
return INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
@ -219,31 +219,44 @@ server_start(struct server *server, const char *serial,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t
|
bool
|
||||||
server_connect_to(struct server *server) {
|
server_connect_to(struct server *server) {
|
||||||
if (!server->tunnel_forward) {
|
if (!server->tunnel_forward) {
|
||||||
server->device_socket = net_accept(server->server_socket);
|
server->video_socket = net_accept(server->server_socket);
|
||||||
|
if (server->video_socket == INVALID_SOCKET) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
server->control_socket = net_accept(server->server_socket);
|
||||||
|
if (server->control_socket == INVALID_SOCKET) {
|
||||||
|
// the video_socket will be clean up on destroy
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't need the server socket anymore
|
||||||
|
close_socket(&server->server_socket);
|
||||||
} else {
|
} else {
|
||||||
uint32_t attempts = 100;
|
uint32_t attempts = 100;
|
||||||
uint32_t delay = 100; // ms
|
uint32_t delay = 100; // ms
|
||||||
server->device_socket = connect_to_server(server->local_port, attempts,
|
server->video_socket =
|
||||||
delay);
|
connect_to_server(server->local_port, attempts, delay);
|
||||||
|
if (server->video_socket == INVALID_SOCKET) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server->device_socket == INVALID_SOCKET) {
|
// we know that the device is listening, we don't need several attempts
|
||||||
return INVALID_SOCKET;
|
server->control_socket =
|
||||||
|
net_connect(IPV4_LOCALHOST, server->local_port);
|
||||||
|
if (server->control_socket == INVALID_SOCKET) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!server->tunnel_forward) {
|
|
||||||
// we don't need the server socket anymore
|
|
||||||
close_socket(&server->server_socket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't need the adb tunnel anymore
|
// we don't need the adb tunnel anymore
|
||||||
disable_tunnel(server); // ignore failure
|
disable_tunnel(server); // ignore failure
|
||||||
server->tunnel_enabled = false;
|
server->tunnel_enabled = false;
|
||||||
|
|
||||||
return server->device_socket;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -268,8 +281,11 @@ server_destroy(struct server *server) {
|
|||||||
if (server->server_socket != INVALID_SOCKET) {
|
if (server->server_socket != INVALID_SOCKET) {
|
||||||
close_socket(&server->server_socket);
|
close_socket(&server->server_socket);
|
||||||
}
|
}
|
||||||
if (server->device_socket != INVALID_SOCKET) {
|
if (server->video_socket != INVALID_SOCKET) {
|
||||||
close_socket(&server->device_socket);
|
close_socket(&server->video_socket);
|
||||||
|
}
|
||||||
|
if (server->control_socket != INVALID_SOCKET) {
|
||||||
|
close_socket(&server->control_socket);
|
||||||
}
|
}
|
||||||
SDL_free(server->serial);
|
SDL_free(server->serial);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@ struct server {
|
|||||||
char *serial;
|
char *serial;
|
||||||
process_t process;
|
process_t process;
|
||||||
socket_t server_socket; // only used if !tunnel_forward
|
socket_t server_socket; // only used if !tunnel_forward
|
||||||
socket_t device_socket;
|
socket_t video_socket;
|
||||||
|
socket_t control_socket;
|
||||||
uint16_t local_port;
|
uint16_t local_port;
|
||||||
bool tunnel_enabled;
|
bool tunnel_enabled;
|
||||||
bool tunnel_forward; // use "adb forward" instead of "adb reverse"
|
bool tunnel_forward; // use "adb forward" instead of "adb reverse"
|
||||||
@ -22,7 +23,8 @@ struct server {
|
|||||||
.serial = NULL, \
|
.serial = NULL, \
|
||||||
.process = PROCESS_NONE, \
|
.process = PROCESS_NONE, \
|
||||||
.server_socket = INVALID_SOCKET, \
|
.server_socket = INVALID_SOCKET, \
|
||||||
.device_socket = INVALID_SOCKET, \
|
.video_socket = INVALID_SOCKET, \
|
||||||
|
.control_socket = INVALID_SOCKET, \
|
||||||
.local_port = 0, \
|
.local_port = 0, \
|
||||||
.tunnel_enabled = false, \
|
.tunnel_enabled = false, \
|
||||||
.tunnel_forward = false, \
|
.tunnel_forward = false, \
|
||||||
@ -40,7 +42,7 @@ server_start(struct server *server, const char *serial,
|
|||||||
const char *crop, bool send_frame_meta);
|
const char *crop, bool send_frame_meta);
|
||||||
|
|
||||||
// block until the communication with the server is established
|
// block until the communication with the server is established
|
||||||
socket_t
|
bool
|
||||||
server_connect_to(struct server *server);
|
server_connect_to(struct server *server);
|
||||||
|
|
||||||
// disconnect and kill the server process
|
// disconnect and kill the server process
|
||||||
|
@ -5,21 +5,21 @@
|
|||||||
|
|
||||||
static void test_control_event_queue_empty(void) {
|
static void test_control_event_queue_empty(void) {
|
||||||
struct control_event_queue queue;
|
struct control_event_queue queue;
|
||||||
SDL_bool init_ok = control_event_queue_init(&queue);
|
bool init_ok = control_event_queue_init(&queue);
|
||||||
assert(init_ok);
|
assert(init_ok);
|
||||||
|
|
||||||
assert(control_event_queue_is_empty(&queue));
|
assert(control_event_queue_is_empty(&queue));
|
||||||
|
|
||||||
struct control_event dummy_event;
|
struct control_event dummy_event;
|
||||||
SDL_bool push_ok = control_event_queue_push(&queue, &dummy_event);
|
bool push_ok = control_event_queue_push(&queue, &dummy_event);
|
||||||
assert(push_ok);
|
assert(push_ok);
|
||||||
assert(!control_event_queue_is_empty(&queue));
|
assert(!control_event_queue_is_empty(&queue));
|
||||||
|
|
||||||
SDL_bool take_ok = control_event_queue_take(&queue, &dummy_event);
|
bool take_ok = control_event_queue_take(&queue, &dummy_event);
|
||||||
assert(take_ok);
|
assert(take_ok);
|
||||||
assert(control_event_queue_is_empty(&queue));
|
assert(control_event_queue_is_empty(&queue));
|
||||||
|
|
||||||
SDL_bool take_empty_ok = control_event_queue_take(&queue, &dummy_event);
|
bool take_empty_ok = control_event_queue_take(&queue, &dummy_event);
|
||||||
assert(!take_empty_ok); // the queue is empty
|
assert(!take_empty_ok); // the queue is empty
|
||||||
|
|
||||||
control_event_queue_destroy(&queue);
|
control_event_queue_destroy(&queue);
|
||||||
@ -27,7 +27,7 @@ static void test_control_event_queue_empty(void) {
|
|||||||
|
|
||||||
static void test_control_event_queue_full(void) {
|
static void test_control_event_queue_full(void) {
|
||||||
struct control_event_queue queue;
|
struct control_event_queue queue;
|
||||||
SDL_bool init_ok = control_event_queue_init(&queue);
|
bool init_ok = control_event_queue_init(&queue);
|
||||||
assert(init_ok);
|
assert(init_ok);
|
||||||
|
|
||||||
assert(!control_event_queue_is_full(&queue));
|
assert(!control_event_queue_is_full(&queue));
|
||||||
@ -36,7 +36,7 @@ static void test_control_event_queue_full(void) {
|
|||||||
// fill the queue
|
// fill the queue
|
||||||
while (control_event_queue_push(&queue, &dummy_event));
|
while (control_event_queue_push(&queue, &dummy_event));
|
||||||
|
|
||||||
SDL_bool take_ok = control_event_queue_take(&queue, &dummy_event);
|
bool take_ok = control_event_queue_take(&queue, &dummy_event);
|
||||||
assert(take_ok);
|
assert(take_ok);
|
||||||
assert(!control_event_queue_is_full(&queue));
|
assert(!control_event_queue_is_full(&queue));
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ static void test_control_event_queue_full(void) {
|
|||||||
|
|
||||||
static void test_control_event_queue_push_take(void) {
|
static void test_control_event_queue_push_take(void) {
|
||||||
struct control_event_queue queue;
|
struct control_event_queue queue;
|
||||||
SDL_bool init_ok = control_event_queue_init(&queue);
|
bool init_ok = control_event_queue_init(&queue);
|
||||||
assert(init_ok);
|
assert(init_ok);
|
||||||
|
|
||||||
struct control_event event = {
|
struct control_event event = {
|
||||||
@ -57,7 +57,7 @@ static void test_control_event_queue_push_take(void) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_bool push1_ok = control_event_queue_push(&queue, &event);
|
bool push1_ok = control_event_queue_push(&queue, &event);
|
||||||
assert(push1_ok);
|
assert(push1_ok);
|
||||||
|
|
||||||
event = (struct control_event) {
|
event = (struct control_event) {
|
||||||
@ -67,11 +67,11 @@ static void test_control_event_queue_push_take(void) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_bool push2_ok = control_event_queue_push(&queue, &event);
|
bool push2_ok = control_event_queue_push(&queue, &event);
|
||||||
assert(push2_ok);
|
assert(push2_ok);
|
||||||
|
|
||||||
// overwrite event
|
// overwrite event
|
||||||
SDL_bool take1_ok = control_event_queue_take(&queue, &event);
|
bool take1_ok = control_event_queue_take(&queue, &event);
|
||||||
assert(take1_ok);
|
assert(take1_ok);
|
||||||
assert(event.type == CONTROL_EVENT_TYPE_KEYCODE);
|
assert(event.type == CONTROL_EVENT_TYPE_KEYCODE);
|
||||||
assert(event.keycode_event.action == AKEY_EVENT_ACTION_DOWN);
|
assert(event.keycode_event.action == AKEY_EVENT_ACTION_DOWN);
|
||||||
@ -79,7 +79,7 @@ static void test_control_event_queue_push_take(void) {
|
|||||||
assert(event.keycode_event.metastate == (AMETA_CTRL_LEFT_ON | AMETA_CTRL_ON));
|
assert(event.keycode_event.metastate == (AMETA_CTRL_LEFT_ON | AMETA_CTRL_ON));
|
||||||
|
|
||||||
// overwrite event
|
// overwrite event
|
||||||
SDL_bool take2_ok = control_event_queue_take(&queue, &event);
|
bool take2_ok = control_event_queue_take(&queue, &event);
|
||||||
assert(take2_ok);
|
assert(take2_ok);
|
||||||
assert(event.type == CONTROL_EVENT_TYPE_TEXT);
|
assert(event.type == CONTROL_EVENT_TYPE_TEXT);
|
||||||
assert(!strcmp(event.text_event.text, "abc"));
|
assert(!strcmp(event.text_event.text, "abc"));
|
||||||
|
@ -16,16 +16,20 @@ public final class DesktopConnection implements Closeable {
|
|||||||
|
|
||||||
private static final String SOCKET_NAME = "scrcpy";
|
private static final String SOCKET_NAME = "scrcpy";
|
||||||
|
|
||||||
private final LocalSocket socket;
|
private final LocalSocket videoSocket;
|
||||||
private final InputStream inputStream;
|
private final FileDescriptor videoFd;
|
||||||
private final FileDescriptor fd;
|
|
||||||
|
private final LocalSocket controlSocket;
|
||||||
|
private final InputStream controlInputStream;
|
||||||
|
|
||||||
|
|
||||||
private final ControlEventReader reader = new ControlEventReader();
|
private final ControlEventReader reader = new ControlEventReader();
|
||||||
|
|
||||||
private DesktopConnection(LocalSocket socket) throws IOException {
|
private DesktopConnection(LocalSocket videoSocket, LocalSocket controlSocket) throws IOException {
|
||||||
this.socket = socket;
|
this.videoSocket = videoSocket;
|
||||||
inputStream = socket.getInputStream();
|
this.controlSocket = controlSocket;
|
||||||
fd = socket.getFileDescriptor();
|
controlInputStream = controlSocket.getInputStream();
|
||||||
|
videoFd = videoSocket.getFileDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LocalSocket connect(String abstractName) throws IOException {
|
private static LocalSocket connect(String abstractName) throws IOException {
|
||||||
@ -44,25 +48,46 @@ public final class DesktopConnection implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
|
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
|
||||||
LocalSocket socket;
|
LocalSocket videoSocket;
|
||||||
|
LocalSocket controlSocket;
|
||||||
if (tunnelForward) {
|
if (tunnelForward) {
|
||||||
socket = listenAndAccept(SOCKET_NAME);
|
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
|
||||||
|
try {
|
||||||
|
videoSocket = localServerSocket.accept();
|
||||||
// send one byte so the client may read() to detect a connection error
|
// send one byte so the client may read() to detect a connection error
|
||||||
socket.getOutputStream().write(0);
|
videoSocket.getOutputStream().write(0);
|
||||||
|
try {
|
||||||
|
controlSocket = localServerSocket.accept();
|
||||||
|
} catch (IOException | RuntimeException e) {
|
||||||
|
videoSocket.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
localServerSocket.close();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
socket = connect(SOCKET_NAME);
|
videoSocket = connect(SOCKET_NAME);
|
||||||
|
try {
|
||||||
|
controlSocket = connect(SOCKET_NAME);
|
||||||
|
} catch (IOException | RuntimeException e) {
|
||||||
|
videoSocket.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DesktopConnection connection = new DesktopConnection(socket);
|
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
|
||||||
Size videoSize = device.getScreenInfo().getVideoSize();
|
Size videoSize = device.getScreenInfo().getVideoSize();
|
||||||
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
socket.shutdownInput();
|
videoSocket.shutdownInput();
|
||||||
socket.shutdownOutput();
|
videoSocket.shutdownOutput();
|
||||||
socket.close();
|
videoSocket.close();
|
||||||
|
controlSocket.shutdownInput();
|
||||||
|
controlSocket.shutdownOutput();
|
||||||
|
controlSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
@ -78,17 +103,17 @@ public final class DesktopConnection implements Closeable {
|
|||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
|
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
|
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
|
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
|
||||||
IO.writeFully(fd, buffer, 0, buffer.length);
|
IO.writeFully(videoFd, buffer, 0, buffer.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileDescriptor getFd() {
|
public FileDescriptor getVideoFd() {
|
||||||
return fd;
|
return videoFd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlEvent receiveControlEvent() throws IOException {
|
public ControlEvent receiveControlEvent() throws IOException {
|
||||||
ControlEvent event = reader.next();
|
ControlEvent event = reader.next();
|
||||||
while (event == null) {
|
while (event == null) {
|
||||||
reader.readFrom(inputStream);
|
reader.readFrom(controlInputStream);
|
||||||
event = reader.next();
|
event = reader.next();
|
||||||
}
|
}
|
||||||
return event;
|
return event;
|
||||||
|
@ -24,7 +24,7 @@ public final class Server {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// synchronous
|
// synchronous
|
||||||
screenEncoder.streamScreen(device, connection.getFd());
|
screenEncoder.streamScreen(device, connection.getVideoFd());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// this is expected on close
|
// this is expected on close
|
||||||
Ln.d("Screen streaming stopped");
|
Ln.d("Screen streaming stopped");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user