Skip to content

Commit 89f6a3c

Browse files
committed
Handle resized video stream
Accept a parameter to limit the video size. For instance, with "-m 960", the great side of the video will be scaled down to 960 (if necessary), while the other side will be scaled down so that the aspect ratio is preserved. Both dimensions must be a multiple of 8, so black bands might be added, and the mouse positions must be computed accordingly.
1 parent 2c4ea68 commit 89f6a3c

18 files changed

+270
-83
lines changed

Diff for: app/src/main.c

+18-3
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@
99
struct args {
1010
const char *serial;
1111
Uint16 port;
12+
Uint16 maximum_size;
1213
};
1314

1415
int parse_args(struct args *args, int argc, char *argv[]) {
1516
int c;
16-
while ((c = getopt(argc, argv, "p:")) != -1) {
17+
while ((c = getopt(argc, argv, "p:m:")) != -1) {
1718
switch (c) {
1819
case 'p': {
1920
char *endptr;
20-
long int value = strtol(optarg, &endptr, 0);
21+
long value = strtol(optarg, &endptr, 0);
2122
if (*optarg == '\0' || *endptr != '\0') {
2223
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid port: %s\n", optarg);
2324
return -1;
@@ -29,6 +30,20 @@ int parse_args(struct args *args, int argc, char *argv[]) {
2930
args->port = (Uint16) value;
3031
break;
3132
}
33+
case 'm': {
34+
char *endptr;
35+
long value = strtol(optarg, &endptr, 0);
36+
if (*optarg == '\0' || *endptr != '\0') {
37+
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid maximum size: %s\n", optarg);
38+
return -1;
39+
}
40+
if (value & ~0xffff) {
41+
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Maximum size must be between 0 and 65535: %ld\n", value);
42+
return -1;
43+
}
44+
args->maximum_size = (Uint16) value;
45+
break;
46+
}
3247
default:
3348
// getopt prints the error message on stderr
3449
return -1;
@@ -65,7 +80,7 @@ int main(int argc, char *argv[]) {
6580

6681
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
6782

68-
res = scrcpy(args.serial, args.port) ? 0 : 1;
83+
res = scrcpy(args.serial, args.port, args.maximum_size) ? 0 : 1;
6984

7085
avformat_network_deinit(); // ignore failure
7186

Diff for: app/src/scrcpy.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ void event_loop(void) {
382382
}
383383
}
384384

385-
SDL_bool scrcpy(const char *serial, Uint16 local_port) {
385+
SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 maximum_size) {
386386
SDL_bool ret = 0;
387387

388388
process_t push_proc = push_server(serial);
@@ -402,7 +402,7 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port) {
402402
}
403403

404404
// server will connect to our socket
405-
process_t server = start_server(serial);
405+
process_t server = start_server(serial, maximum_size);
406406
if (server == PROCESS_NONE) {
407407
ret = SDL_FALSE;
408408
SDLNet_TCP_Close(server_socket);

Diff for: app/src/scrcpy.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44
#include <SDL2/SDL_stdinc.h>
55

6-
SDL_bool scrcpy(const char *serial, Uint16 local_port);
6+
SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 maximum_size);
77

88
#endif

Diff for: app/src/server.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ process_t disable_tunnel(const char *serial) {
2121
return adb_reverse_remove(serial, SOCKET_NAME);
2222
}
2323

24-
process_t start_server(const char *serial) {
24+
process_t start_server(const char *serial, Uint16 maximum_size) {
25+
char maximum_size_string[6];
26+
sprintf(maximum_size_string, "%d", maximum_size);
2527
const char *const cmd[] = {
2628
"shell",
2729
"CLASSPATH=/data/local/tmp/scrcpy-server.jar",
2830
"app_process",
2931
"/system/bin",
30-
"com.genymobile.scrcpy.ScrCpyServer"
32+
"com.genymobile.scrcpy.ScrCpyServer",
33+
maximum_size_string,
3134
};
3235
return adb_execute(serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
3336
}

Diff for: app/src/server.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ process_t push_server(const char *serial);
44
process_t enable_tunnel(const char *serial, Uint16 local_port);
55
process_t disable_tunnel(const char *serial);
66

7-
process_t start_server(const char *serial);
7+
process_t start_server(const char *serial, Uint16 maximum_size);
88
void stop_server(process_t server);

Diff for: server/Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ SRC := com/genymobile/scrcpy/ScrCpyServer.java \
2424
com/genymobile/scrcpy/ControlEventReader.java \
2525
com/genymobile/scrcpy/DesktopConnection.java \
2626
com/genymobile/scrcpy/Device.java \
27+
com/genymobile/scrcpy/DisplayInfo.java \
2728
com/genymobile/scrcpy/EventController.java \
2829
com/genymobile/scrcpy/Ln.java \
30+
com/genymobile/scrcpy/Options.java \
2931
com/genymobile/scrcpy/Point.java \
3032
com/genymobile/scrcpy/Position.java \
3133
com/genymobile/scrcpy/ScreenInfo.java \
3234
com/genymobile/scrcpy/ScreenStreamer.java \
3335
com/genymobile/scrcpy/ScreenStreamerSession.java \
36+
com/genymobile/scrcpy/Size.java \
3437
com/genymobile/scrcpy/wrappers/DisplayManager.java \
3538
com/genymobile/scrcpy/wrappers/InputManager.java \
3639
com/genymobile/scrcpy/wrappers/ServiceManager.java \

Diff for: server/src/com/genymobile/scrcpy/DesktopConnection.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,9 @@ private static LocalSocket connect(String abstractName) throws IOException {
3636
public static DesktopConnection open(Device device) throws IOException {
3737
LocalSocket socket = connect(SOCKET_NAME);
3838

39-
ScreenInfo initialScreenInfo = device.getScreenInfo();
40-
int width = initialScreenInfo.getLogicalWidth();
41-
int height = initialScreenInfo.getLogicalHeight();
42-
4339
DesktopConnection connection = new DesktopConnection(socket);
44-
connection.send(Device.getDeviceName(), width, height);
40+
Size videoSize = device.getScreenInfo().getVideoSize();
41+
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
4542
return connection;
4643
}
4744

@@ -81,4 +78,3 @@ public ControlEvent receiveControlEvent() throws IOException {
8178
return event;
8279
}
8380
}
84-

Diff for: server/src/com/genymobile/scrcpy/Device.java

+51-16
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import com.genymobile.scrcpy.wrappers.InputManager;
88
import com.genymobile.scrcpy.wrappers.ServiceManager;
99

10-
public class Device {
10+
public final class Device {
1111

1212
public interface RotationListener {
1313
void onRotationChanged(int rotation);
@@ -18,13 +18,12 @@ public interface RotationListener {
1818
private ScreenInfo screenInfo;
1919
private RotationListener rotationListener;
2020

21-
public Device() {
22-
screenInfo = readScreenInfo();
21+
public Device(Options options) {
22+
screenInfo = computeScreenInfo(options.getMaximumSize());
2323
registerRotationWatcher(new IRotationWatcher.Stub() {
2424
@Override
2525
public void onRotationChanged(int rotation) throws RemoteException {
2626
synchronized (Device.this) {
27-
// update screenInfo cache
2827
screenInfo = screenInfo.withRotation(rotation);
2928

3029
// notify
@@ -37,23 +36,59 @@ public void onRotationChanged(int rotation) throws RemoteException {
3736
}
3837

3938
public synchronized ScreenInfo getScreenInfo() {
40-
if (screenInfo == null) {
41-
screenInfo = readScreenInfo();
42-
}
4339
return screenInfo;
4440
}
4541

46-
public Point getPhysicalPoint(Position position) {
47-
ScreenInfo screenInfo = getScreenInfo();
48-
int deviceWidth = screenInfo.getLogicalWidth();
49-
int deviceHeight = screenInfo.getLogicalHeight();
50-
int scaledX = position.getX() * deviceWidth / position.getScreenWidth();
51-
int scaledY = position.getY() * deviceHeight / position.getScreenHeight();
52-
return new Point(scaledX, scaledY);
42+
private ScreenInfo computeScreenInfo(int maximumSize) {
43+
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo();
44+
boolean rotated = (displayInfo.getRotation() & 1) != 0;
45+
Size deviceSize = displayInfo.getSize();
46+
int w = deviceSize.getWidth();
47+
int h = deviceSize.getHeight();
48+
int padding = 0;
49+
if (maximumSize > 0) {
50+
assert maximumSize % 8 == 0;
51+
boolean portrait = h > w;
52+
int major = portrait ? h : w;
53+
int minor = portrait ? w : h;
54+
if (major > maximumSize) {
55+
int minorExact = minor * maximumSize / major;
56+
// +7 to ceil the value on rounding to the next multiple of 8,
57+
// so that any necessary black bands to keep the aspect ratio are added to the smallest dimension
58+
minor = (minorExact + 7) & ~7;
59+
major = maximumSize;
60+
padding = minor - minorExact;
61+
}
62+
w = portrait ? minor : major;
63+
h = portrait ? major : minor;
64+
}
65+
return new ScreenInfo(deviceSize, new Size(w, h), padding, rotated);
5366
}
5467

55-
private ScreenInfo readScreenInfo() {
56-
return serviceManager.getDisplayManager().getScreenInfo();
68+
public Point getPhysicalPoint(Position position) {
69+
ScreenInfo screenInfo = getScreenInfo(); // read with synchronization
70+
Size videoSize = screenInfo.getVideoSize();
71+
Size clientVideoSize = position.getScreenSize();
72+
if (!videoSize.equals(clientVideoSize)) {
73+
// The client sends a click relative to a video with wrong dimensions,
74+
// the device may have been rotated since the event was generated, so ignore the event
75+
return null;
76+
}
77+
Size deviceSize = screenInfo.getDeviceSize();
78+
int xPadding = screenInfo.getXPadding();
79+
int yPadding = screenInfo.getYPadding();
80+
int contentWidth = videoSize.getWidth() - xPadding;
81+
int contentHeight = videoSize.getHeight() - yPadding;
82+
Point point = position.getPoint();
83+
int x = point.getX() - xPadding / 2;
84+
int y = point.getY() - yPadding / 2;
85+
if (x < 0 || x >= contentWidth || y < 0 || y >= contentHeight) {
86+
// out of screen
87+
return null;
88+
}
89+
int scaledX = x * deviceSize.getWidth() / videoSize.getWidth();
90+
int scaledY = y * deviceSize.getHeight() / videoSize.getHeight();
91+
return new Point(scaledX, scaledY);
5792
}
5893

5994
public static String getDeviceName() {

Diff for: server/src/com/genymobile/scrcpy/DisplayInfo.java

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.genymobile.scrcpy;
2+
3+
public final class DisplayInfo {
4+
private final Size size;
5+
private final int rotation;
6+
7+
public DisplayInfo(Size size, int rotation) {
8+
this.size = size;
9+
this.rotation = rotation;
10+
}
11+
12+
public Size getSize() {
13+
return size;
14+
}
15+
16+
public int getRotation() {
17+
return rotation;
18+
}
19+
}
20+

Diff for: server/src/com/genymobile/scrcpy/Options.java

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.genymobile.scrcpy;
2+
3+
public class Options {
4+
private int maximumSize;
5+
6+
public int getMaximumSize() {
7+
return maximumSize;
8+
}
9+
10+
public void setMaximumSize(int maximumSize) {
11+
this.maximumSize = maximumSize;
12+
}
13+
}

Diff for: server/src/com/genymobile/scrcpy/Point.java

+16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.genymobile.scrcpy;
22

3+
import java.util.Objects;
4+
35
public class Point {
46
private int x;
57
private int y;
@@ -17,6 +19,20 @@ public int getY() {
1719
return y;
1820
}
1921

22+
@Override
23+
public boolean equals(Object o) {
24+
if (this == o) return true;
25+
if (o == null || getClass() != o.getClass()) return false;
26+
Point point = (Point) o;
27+
return x == point.x &&
28+
y == point.y;
29+
}
30+
31+
@Override
32+
public int hashCode() {
33+
return Objects.hash(x, y);
34+
}
35+
2036
@Override
2137
public String toString() {
2238
return "Point{" +

Diff for: server/src/com/genymobile/scrcpy/Position.java

+27-21
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,48 @@
11
package com.genymobile.scrcpy;
22

3+
import java.util.Objects;
4+
35
public class Position {
6+
private Point point;
7+
private Size screenSize;
48

5-
private int x;
6-
private int y;
7-
private int screenWidth;
8-
private int screenHeight;
9+
public Position(Point point, Size screenSize) {
10+
this.point = point;
11+
this.screenSize = screenSize;
12+
}
913

1014
public Position(int x, int y, int screenWidth, int screenHeight) {
11-
this.x = x;
12-
this.y = y;
13-
this.screenWidth = screenWidth;
14-
this.screenHeight = screenHeight;
15+
this(new Point(x, y), new Size(screenWidth, screenHeight));
1516
}
1617

17-
public int getX() {
18-
return x;
18+
public Point getPoint() {
19+
return point;
1920
}
2021

21-
public int getY() {
22-
return y;
22+
public Size getScreenSize() {
23+
return screenSize;
2324
}
2425

25-
public int getScreenWidth() {
26-
return screenWidth;
26+
@Override
27+
public boolean equals(Object o) {
28+
if (this == o) return true;
29+
if (o == null || getClass() != o.getClass()) return false;
30+
Position position = (Position) o;
31+
return Objects.equals(point, position.point) &&
32+
Objects.equals(screenSize, position.screenSize);
2733
}
2834

29-
public int getScreenHeight() {
30-
return screenHeight;
35+
@Override
36+
public int hashCode() {
37+
return Objects.hash(point, screenSize);
3138
}
3239

3340
@Override
3441
public String toString() {
35-
return "Point{" +
36-
"x=" + x +
37-
", y=" + y +
38-
", screenWidth=" + screenWidth +
39-
", screenHeight=" + screenHeight +
42+
return "Position{" +
43+
"point=" + point +
44+
", screenSize=" + screenSize +
4045
'}';
4146
}
47+
4248
}

Diff for: server/src/com/genymobile/scrcpy/ScrCpyServer.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ public class ScrCpyServer {
66

77
private static final String TAG = "scrcpy";
88

9-
private static void scrcpy() throws IOException {
10-
final Device device = new Device();
9+
private static void scrcpy(Options options) throws IOException {
10+
final Device device = new Device(options);
1111
try (DesktopConnection connection = DesktopConnection.open(device)) {
12-
final ScreenStreamer streamer = new ScreenStreamer(connection);
12+
final ScreenStreamer streamer = new ScreenStreamer(device, connection);
1313
device.setRotationListener(new Device.RotationListener() {
1414
@Override
1515
public void onRotationChanged(int rotation) {
@@ -43,8 +43,13 @@ public void run() {
4343
}
4444

4545
public static void main(String... args) throws Exception {
46+
Options options = new Options();
47+
if (args.length > 0) {
48+
int maximumSize = Integer.parseInt(args[0]) & ~7; // multiple of 8
49+
options.setMaximumSize(maximumSize);
50+
}
4651
try {
47-
scrcpy();
52+
scrcpy(options);
4853
} catch (Throwable t) {
4954
t.printStackTrace();
5055
throw t;

0 commit comments

Comments
 (0)