Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client orientation (resolves #218) #1151

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DEVELOP.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ The client uses 4 threads:
recording,
- the **controller** thread, sending _control messages_ to the server,
- the **receiver** thread (managed by the controller), receiving _device
messages_ from the client.
messages_ from the server.

In addition, another thread can be started if necessary to handle APK
installation or file push requests (via drag&drop on the main window) or to
Expand All @@ -214,7 +214,7 @@ When a new decoded frame is available, the decoder _swaps_ the decoding and
rendering frame (with proper synchronization). Thus, it immediatly starts
to decode a new frame while the main thread renders the last one.

If a [recorder] is present (i.e. `--record` is enabled), then its muxes the raw
If a [recorder] is present (i.e. `--record` is enabled), then it muxes the raw
H.264 packet to the output video file.

[stream]: https://github.com/Genymobile/scrcpy/blob/ffe0417228fb78ab45b7ee4e202fc06fc8875bf3/app/src/stream.h
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ The initial window position and size may be specified:
scrcpy --window-x 100 --window-y 100 --window-width 800 --window-height 600
```

#### Lock orientation

Keep the window in landscape orientation:

```bash
scrcpy --orientation 3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tryed

ctrl+r

C:\scr>scrcpy --orientation 3
scrcpy: unknown option -- orientation

C:\scr>scrcpy --lock-video-orientation 1
scrcpy: unknown option -- lock-video-orientation

but not go in landscape in some apps like this

https://i.imgur.com/lKPzlEg.jpg

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using the dev branch? This is not merged into master yet.

Cheers

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, any guide for build exe for win10 64bit?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

@LEENO82 LEENO82 Mar 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to fix this error?

root@debian:/home/debian/Downloads/scr/scr# make -f Makefile.CrossWindows
fatal: Not a git repository (or any of the parent directories): .git
./gradlew clean

BUILD SUCCESSFUL in 1s
1 actionable task: 1 up-to-date
rm -rf "build-server" "build-win32" "build-win64" \
	   "build-win32-noconsole" "build-win64-noconsole" "dist"
[ -d "build-server" ] || ( mkdir "build-server" && \
	meson "build-server" \
		--buildtype release -Dcompile_app=false )
The Meson build system
Version: 0.53.2
Source dir: /home/debian/Downloads/scr/scr
Build dir: /home/debian/Downloads/scr/scr/build-server
Build type: native build
Project name: scrcpy
Project version: 1.12.1
C compiler for the host machine: cc (gcc 6.3.0 "cc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516")
C linker for the host machine: cc ld.bfd 2.28
Host machine cpu family: x86_64
Host machine cpu: x86_64
Program ./scripts/build-wrapper.sh found: YES (/bin/bash /home/debian/Downloads/scr/scr/server/./scripts/build-wrapper.sh)
WARNING: Project targeting '>= 0.37' but tried to use feature introduced in '0.48.0': console arg in custom_target
DEPRECATION: build_always is deprecated. Combine build_by_default and build_always_stale instead.
Build targets in project: 2
WARNING: Project specifies a minimum meson_version '>= 0.37' but uses features which were added in newer versions:
 * 0.48.0: {'console arg in custom_target'}

Found ninja-1.7.2 at /usr/bin/ninja
ninja -C "build-server"
ninja: Entering directory `build-server'
[0/1] Generating scrcpy-server with a custom command.
(not invoking gradle, since we are root)
make -C prebuilt-deps prepare-win32
make[1]: Entering directory '/home/debian/Downloads/scr/scr/prebuilt-deps'
make[1]: execvp: ./prepare-dep: Permission denied
Makefile:33: recipe for target 'prepare-sdl2' failed
make[1]: *** [prepare-sdl2] Error 127
make[1]: Leaving directory '/home/debian/Downloads/scr/scr/prebuilt-deps'
Makefile.CrossWindows:51: recipe for target 'prepare-deps-win32' failed
make: [prepare-deps-win32] Error 2 (ignored)
[ -d "build-win32" ] || ( mkdir "build-win32" && \
	meson "build-win32" \
		--cross-file cross_win32.txt \
		--buildtype release --strip -Db_lto=true \
		-Dcrossbuild_windows=true \
		-Dcompile_server=false \
		-Dportable=true )
The Meson build system
Version: 0.53.2
Source dir: /home/debian/Downloads/scr/scr
Build dir: /home/debian/Downloads/scr/scr/build-win32
Build type: cross build
Project name: scrcpy
Project version: 1.12.1
C compiler for the build machine: cc (gcc 6.3.0 "cc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516")
C linker for the build machine: cc ld.bfd 2.28
C compiler for the host machine: /usr/bin/i686-w64-mingw32-gcc (gcc 6.3.0 "i686-w64-mingw32-gcc (GCC) 6.3.0 20170516")
C linker for the host machine: /usr/bin/i686-w64-mingw32-gcc ld.bfd 2.28
Build machine cpu family: x86_64
Build machine cpu: x86_64
Host machine cpu family: x86
Host machine cpu: i686
Target machine cpu family: x86
Target machine cpu: i686

app/meson.build:46:4: ERROR: Include dir ../prebuilt-deps/SDL2-2.0.10/i686-w64-mingw32/include does not exist.

A full log can be found at /home/debian/Downloads/scr/scr/build-win32/meson-logs/meson-log.txt
Makefile.CrossWindows:54: recipe for target 'build-win32' failed
make: *** [build-win32] Error 1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not related to the pull request. I don't know how to build for windows either. I know you should not be building as root though. Please ask your question in an appropriate thread so people can help you.

PS: You should also checkout the dev branch before you run the build since --lock-video-orientation is what you are after.

Thanks

```

#### Borderless

To disable window decorations:
Expand Down
5 changes: 5 additions & 0 deletions app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ conf.set('DEFAULT_LOCAL_PORT', '27183')
# overridden by option --max-size
conf.set('DEFAULT_MAX_SIZE', '0') # 0: unlimited

# the default client orientation
# natural device orientation is 0 and each increment adds 90 degrees
# overridden by option --orientation
conf.set('DEFAULT_ORIENTATION', '-1') # -1: unlocked

# the default video bitrate, in bits/second
# overridden by option --bit-rate
conf.set('DEFAULT_BIT_RATE', '8000000') # 8Mbps
Expand Down
10 changes: 9 additions & 1 deletion app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ Default is 8000000.
.BI "\-\-crop " width\fR:\fIheight\fR:\fIx\fR:\fIy
Crop the device screen on the server.

The values are expressed in the device natural orientation (typically, portrait for a phone, landscape for a tablet). Any
The values are expressed in the device natural clientOrientation (typically, portrait for a phone, landscape for a tablet) unless
.B \-\-orientation
is specified. Any
.B \-\-max\-size
value is computed on the cropped size.

Expand All @@ -51,6 +53,12 @@ Limit both the width and height of the video to \fIvalue\fR. The other dimension

Default is 0 (unlimited).

.TP
.BI "\-o, \-\-orientation " value
Lock client orientation to \fIvalue\fR. Values are integers in the range [0..3]. Natural device orientation is 0 and each increment adds 90 degrees.

Default is -1 (unlocked).

.TP
.B \-n, \-\-no\-control
Disable device control (mirror the device in read\-only).
Expand Down
28 changes: 27 additions & 1 deletion app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ scrcpy_print_usage(const char *arg0) {
" is preserved.\n"
" Default is %d%s.\n"
"\n"
" -o, --orientation value\n"
" Lock client orientation to value. Values are integers in the\n"
" range [0..3]. Natural device orientation is 0 and each\n"
" increment adds 90 degrees.\n"
" Default is %d%s.\n"
"\n"
" -n, --no-control\n"
" Disable device control (mirror the device in read-only).\n"
"\n"
Expand Down Expand Up @@ -193,6 +199,7 @@ scrcpy_print_usage(const char *arg0) {
arg0,
DEFAULT_BIT_RATE,
DEFAULT_MAX_SIZE, DEFAULT_MAX_SIZE ? "" : " (unlimited)",
DEFAULT_ORIENTATION, DEFAULT_ORIENTATION >= 0 ? "" : " (unlocked)",
DEFAULT_LOCAL_PORT);
}

Expand Down Expand Up @@ -259,6 +266,19 @@ parse_max_fps(const char *s, uint16_t *max_fps) {
return true;
}

static bool
parse_orientation(const char *s, int8_t *orientation) {
long value;
bool ok = parse_integer_arg(s, &value, false, -1, 3,
"orientation");
if (!ok) {
return false;
}

*orientation = (int8_t) value;
return true;
}

static bool
parse_window_position(const char *s, int16_t *position) {
long value;
Expand Down Expand Up @@ -351,6 +371,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"help", no_argument, NULL, 'h'},
{"max-fps", required_argument, NULL, OPT_MAX_FPS},
{"max-size", required_argument, NULL, 'm'},
{"orientation", required_argument, NULL, 'o'},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's an "advanced" option, I'd prefer not to "consume" a short option (letter) for it.

I initially assigned a short letter for everything, but since few releases, I assigned only long options to "advanced" features.

{"no-control", no_argument, NULL, 'n'},
{"no-display", no_argument, NULL, 'N'},
{"port", required_argument, NULL, 'p'},
Expand Down Expand Up @@ -379,7 +400,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
optind = 0; // reset to start from the first argument in tests

int c;
while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv", long_options,
while ((c = getopt_long(argc, argv, "b:c:fF:hm:o:nNp:r:s:StTv", long_options,
NULL)) != -1) {
switch (c) {
case 'b':
Expand Down Expand Up @@ -417,6 +438,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
return false;
}
break;
case 'o':
if(!parse_orientation(optarg, &opts->orientation)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ( (with a space)

return false;
}
break;
case 'n':
opts->control = false;
break;
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ scrcpy(const struct scrcpy_options *options) {
.max_size = options->max_size,
.bit_rate = options->bit_rate,
.max_fps = options->max_fps,
.orientation = options->orientation,
.control = options->control,
};
if (!server_start(&server, options->serial, &params)) {
Expand Down
2 changes: 2 additions & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct scrcpy_options {
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;
int8_t orientation;
int16_t window_x;
int16_t window_y;
uint16_t window_width;
Expand All @@ -45,6 +46,7 @@ struct scrcpy_options {
.max_size = DEFAULT_MAX_SIZE, \
.bit_rate = DEFAULT_BIT_RATE, \
.max_fps = 0, \
.orientation = DEFAULT_ORIENTATION, \
.window_x = -1, \
.window_y = -1, \
.window_width = 0, \
Expand Down
3 changes: 3 additions & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,11 @@ execute_server(struct server *server, const struct server_params *params) {
char max_size_string[6];
char bit_rate_string[11];
char max_fps_string[6];
char orientation_string[4];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-128\0 is 5 chars long :)

(OK, this value should not be possible here, but then 4 is also unexpected)

sprintf(max_size_string, "%"PRIu16, params->max_size);
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
sprintf(orientation_string, "%"PRIi8, params->orientation);
const char *const cmd[] = {
"shell",
"CLASSPATH=" DEVICE_SERVER_PATH,
Expand All @@ -142,6 +144,7 @@ execute_server(struct server *server, const struct server_params *params) {
max_size_string,
bit_rate_string,
max_fps_string,
orientation_string,
server->tunnel_forward ? "true" : "false",
params->crop ? params->crop : "-",
"true", // always send frame meta (packet boundaries + timestamp)
Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct server_params {
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;
int8_t orientation;
bool control;
};

Expand Down
2 changes: 2 additions & 0 deletions app/tests/test_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ static void test_options(void) {
"--fullscreen",
"--max-fps", "30",
"--max-size", "1024",
"--orientation", "2",
// "--no-control" is not compatible with "--turn-screen-off"
// "--no-display" is not compatible with "--fulscreen"
"--port", "1234",
Expand Down Expand Up @@ -78,6 +79,7 @@ static void test_options(void) {
assert(opts->fullscreen);
assert(opts->max_fps == 30);
assert(opts->max_size == 1024);
assert(opts->orientation == 2);
assert(opts->port == 1234);
assert(!strcmp(opts->push_target, "/sdcard/Movies"));
assert(!strcmp(opts->record_filename, "file"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ public class ControlMessageReader {
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
private static final int RAW_BUFFER_SIZE = 1024;

private static int rotationOffset = 0;

private final byte[] rawBuffer = new byte[RAW_BUFFER_SIZE];
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
private final byte[] textBuffer = new byte[CLIPBOARD_TEXT_MAX_LENGTH];


public ControlMessageReader() {
// invariant: the buffer is always in "get" mode
buffer.limit(0);
Expand Down Expand Up @@ -169,7 +172,30 @@ private static Position readPosition(ByteBuffer buffer) {
int y = buffer.getInt();
int screenWidth = toUnsigned(buffer.getShort());
int screenHeight = toUnsigned(buffer.getShort());
return new Position(x, y, screenWidth, screenHeight);
return rotatePosition(x, y, screenWidth, screenHeight);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ControlMessageReader is only responsible for deserializing the messages from the client, it should not manage rotation.

I suggest to do this in Device.getPhysicalPoint() instead.

}

@SuppressWarnings("SuspiciousNameCombination")
private static Position rotatePosition(int x, int y, int screenWidth, int screenHeight) {
Position position;
switch (rotationOffset) {
case 1:
position = new Position(y, screenWidth - x, screenHeight, screenWidth);
break;
case 2:
position = new Position(screenWidth - x, screenHeight - y, screenWidth, screenHeight);
break;
case 3:
position = new Position(screenHeight - y, x, screenHeight, screenWidth);
break;
default:
position = new Position(x, y, screenWidth, screenHeight);
}
return position;
}

static void setRotationOffset(int newRotationOffset) {
rotationOffset = newRotationOffset;
}

@SuppressWarnings("checkstyle:MagicNumber")
Expand Down
10 changes: 6 additions & 4 deletions server/src/main/java/com/genymobile/scrcpy/Device.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ public synchronized ScreenInfo getScreenInfo() {

private ScreenInfo computeScreenInfo(Rect crop, int maxSize) {
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo();
boolean rotated = (displayInfo.getRotation() & 1) != 0;
int rotation = displayInfo.getRotation();
Size deviceSize = displayInfo.getSize();
Rect contentRect = new Rect(0, 0, deviceSize.getWidth(), deviceSize.getHeight());
if (crop != null) {
if (rotated) {
if (rotation % 2 != 0 ) { // 180s preserve dimensions
// the crop (provided by the user) is expressed in the natural orientation
crop = flipRect(crop);
}
Expand All @@ -64,7 +64,7 @@ private ScreenInfo computeScreenInfo(Rect crop, int maxSize) {
}

Size videoSize = computeVideoSize(contentRect.width(), contentRect.height(), maxSize);
return new ScreenInfo(contentRect, videoSize, rotated);
return new ScreenInfo(contentRect, videoSize, rotation);
}

private static String formatCrop(Rect rect) {
Expand Down Expand Up @@ -192,7 +192,9 @@ public void rotateDevice() {
}
}

@SuppressWarnings("SuspiciousNameCombination")
static Rect flipRect(Rect crop) {
return new Rect(crop.top, crop.left, crop.bottom, crop.right);
crop.set(crop.top, crop.left, crop.bottom, crop.right);
return crop;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

The parameter should not be modified (it could have unexpected side effects).

}
}
9 changes: 9 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public class Options {
private int maxSize;
private int bitRate;
private int maxFps;
private int clientOrientation;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is confusing: this is not the client orientation, everything is managed by the server.

forcedVideoOrientation? lockedVideoOrientation?

private boolean tunnelForward;
private Rect crop;
private boolean sendFrameMeta; // send PTS so that the client may record properly
Expand Down Expand Up @@ -35,6 +36,14 @@ public void setMaxFps(int maxFps) {
this.maxFps = maxFps;
}

public int getClientOrientation() {
return clientOrientation;
}

public void setClientOrientation(int clientOrientation) {
this.clientOrientation = clientOrientation;
}

public boolean isTunnelForward() {
return tunnelForward;
}
Expand Down
Loading