diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 586378d8..0384187e 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -296,15 +296,13 @@ scrcpy(const struct scrcpy_options *options) { goto finally_destroy_server; } - socket_t device_socket = server.device_socket; - char device_name[DEVICE_NAME_FIELD_LENGTH]; struct size frame_size; // screenrecord does not send frames when the screen content does not // change therefore, we transmit the screen size before the video stream, // 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); ret = false; goto finally_destroy_server; @@ -343,7 +341,7 @@ scrcpy(const struct scrcpy_options *options) { 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 // start the stream @@ -355,7 +353,7 @@ scrcpy(const struct scrcpy_options *options) { if (display) { if (control) { - if (!controller_init(&controller, device_socket)) { + if (!controller_init(&controller, server.control_socket)) { ret = false; goto finally_stop_stream; } diff --git a/app/src/server.c b/app/src/server.c index 87a61fb7..a4f0b35e 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -222,8 +222,14 @@ server_start(struct server *server, const char *serial, bool server_connect_to(struct server *server) { if (!server->tunnel_forward) { - server->device_socket = net_accept(server->server_socket); - if (server->device_socket == INVALID_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; } @@ -232,9 +238,16 @@ server_connect_to(struct server *server) { } else { uint32_t attempts = 100; uint32_t delay = 100; // ms - server->device_socket = connect_to_server(server->local_port, attempts, - delay); - if (server->device_socket == INVALID_SOCKET) { + server->video_socket = + connect_to_server(server->local_port, attempts, delay); + if (server->video_socket == INVALID_SOCKET) { + return false; + } + + // we know that the device is listening, we don't need several attempts + server->control_socket = + net_connect(IPV4_LOCALHOST, server->local_port); + if (server->control_socket == INVALID_SOCKET) { return false; } } @@ -268,8 +281,11 @@ server_destroy(struct server *server) { if (server->server_socket != INVALID_SOCKET) { close_socket(&server->server_socket); } - if (server->device_socket != INVALID_SOCKET) { - close_socket(&server->device_socket); + if (server->video_socket != INVALID_SOCKET) { + close_socket(&server->video_socket); + } + if (server->control_socket != INVALID_SOCKET) { + close_socket(&server->control_socket); } SDL_free(server->serial); } diff --git a/app/src/server.h b/app/src/server.h index 4d16fdab..0c8443bb 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -11,7 +11,8 @@ struct server { char *serial; process_t process; 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; bool tunnel_enabled; bool tunnel_forward; // use "adb forward" instead of "adb reverse" @@ -22,7 +23,8 @@ struct server { .serial = NULL, \ .process = PROCESS_NONE, \ .server_socket = INVALID_SOCKET, \ - .device_socket = INVALID_SOCKET, \ + .video_socket = INVALID_SOCKET, \ + .control_socket = INVALID_SOCKET, \ .local_port = 0, \ .tunnel_enabled = false, \ .tunnel_forward = false, \ diff --git a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java index d87a7fd8..05b2c6ee 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java +++ b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java @@ -16,16 +16,20 @@ public final class DesktopConnection implements Closeable { private static final String SOCKET_NAME = "scrcpy"; - private final LocalSocket socket; - private final InputStream inputStream; - private final FileDescriptor fd; + private final LocalSocket videoSocket; + private final FileDescriptor videoFd; + + private final LocalSocket controlSocket; + private final InputStream controlInputStream; + private final ControlEventReader reader = new ControlEventReader(); - private DesktopConnection(LocalSocket socket) throws IOException { - this.socket = socket; - inputStream = socket.getInputStream(); - fd = socket.getFileDescriptor(); + private DesktopConnection(LocalSocket videoSocket, LocalSocket controlSocket) throws IOException { + this.videoSocket = videoSocket; + this.controlSocket = controlSocket; + controlInputStream = controlSocket.getInputStream(); + videoFd = videoSocket.getFileDescriptor(); } 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 { - LocalSocket socket; + LocalSocket videoSocket; + LocalSocket controlSocket; if (tunnelForward) { - socket = listenAndAccept(SOCKET_NAME); - // send one byte so the client may read() to detect a connection error - socket.getOutputStream().write(0); + LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME); + try { + videoSocket = localServerSocket.accept(); + // send one byte so the client may read() to detect a connection error + videoSocket.getOutputStream().write(0); + try { + controlSocket = localServerSocket.accept(); + } catch (IOException | RuntimeException e) { + videoSocket.close(); + throw e; + } + } finally { + localServerSocket.close(); + } } 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(); connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight()); return connection; } public void close() throws IOException { - socket.shutdownInput(); - socket.shutdownOutput(); - socket.close(); + videoSocket.shutdownInput(); + videoSocket.shutdownOutput(); + videoSocket.close(); + controlSocket.shutdownInput(); + controlSocket.shutdownOutput(); + controlSocket.close(); } @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 + 2] = (byte) (height >> 8); 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() { - return fd; + public FileDescriptor getVideoFd() { + return videoFd; } public ControlEvent receiveControlEvent() throws IOException { ControlEvent event = reader.next(); while (event == null) { - reader.readFrom(inputStream); + reader.readFrom(controlInputStream); event = reader.next(); } return event; diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index b782101c..db192dd1 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -24,7 +24,7 @@ public final class Server { try { // synchronous - screenEncoder.streamScreen(device, connection.getFd()); + screenEncoder.streamScreen(device, connection.getVideoFd()); } catch (IOException e) { // this is expected on close Ln.d("Screen streaming stopped");