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

Conversation

Stamoulohta
Copy link
Contributor

I've added an option to keep the client window in a specific orientation. This helps in recording and in utilizing small screens.

I've tried my best to update any relevant documentation and test files. I could not update the Korean documentation files though.

Thanks,
George

@Stamoulohta Stamoulohta changed the title Client orientation Client orientation (Resolves #218) Feb 16, 2020
@Stamoulohta Stamoulohta changed the title Client orientation (Resolves #218) Client orientation (resolves #218) Feb 16, 2020
rom1v pushed a commit that referenced this pull request Feb 16, 2020
PR #1151 <#1151>

Signed-off-by: Romain Vimont <[email protected]>
Copy link
Collaborator

@rom1v rom1v left a comment

Choose a reason for hiding this comment

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

Thank you for your work!

The behavior is not what I had in mind for #218 (I was thinking about rotating only the window on the client, possibly at any time using shortcuts), but I like the one you propose (and they are not incompatible anyway).

In particular, the rotation is taken into account when recording, and it allows to record with constant frame size (changing frame size on rotation often does not survive re-encoding).

Globally, I'm ok with the PR ✔️ Moreover, the code is clean and documentation and tests are updated 👍

Since there are many orientations, at least:

  • device orientation (the Android orientation)
  • video orientation (the orientation in which the video is captured and sent to the client, this is the target of this PR)
  • window orientation (how the received video is rotated by the client)

I would suggest a more explicit parameter name, like --lock-video-orientation or --force-video-orientation.

I merged the documentation fix on master. But this PR should target dev branch instead (the idea is that master only contains the last releases + bugfixes, and the prebuilt server from the last release must work with the client code, because it's the main page on GitHub).

app/src/cli.c Outdated
@@ -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.

app/src/cli.c Outdated
@@ -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)

app/src/server.c Outdated
@@ -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)

@@ -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.

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).

@@ -27,23 +27,27 @@

private int bitRate;
private int maxFps;
private int clientOrientation;
private int rotationOffset = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

(in Java, 0 is the default value for fields)

@@ -134,6 +139,13 @@ private void writeFrameMeta(FileDescriptor fd, MediaCodec.BufferInfo bufferInfo,
IO.writeFully(fd, headerBuffer);
}

private void setRotationOffset(int rotation) {
if(clientOrientation != -1) {// user has requested orientation
rotationOffset = rotation + clientOrientation % 4;
Copy link
Collaborator

Choose a reason for hiding this comment

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

rotationOffset = (rotation + clientOrientation) % 4;

format.setInteger(MediaFormat.KEY_WIDTH, width);
format.setInteger(MediaFormat.KEY_HEIGHT, height);
private static void setSize(MediaFormat format, int orientation, int width, int height) {
if(orientation % 2 == 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

if (

@@ -134,6 +139,13 @@ private void writeFrameMeta(FileDescriptor fd, MediaCodec.BufferInfo bufferInfo,
IO.writeFully(fd, headerBuffer);
}

private void setRotationOffset(int rotation) {
if(clientOrientation != -1) {// user has requested 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 ( and { //

}

public ScreenInfo withRotation(int newRotation) {
if ((rotation + newRotation) % 2 == 0) { // 180s don't need flipping
return this;
Copy link
Collaborator

Choose a reason for hiding this comment

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

But then the returned ScreenInfo will not have the correct rotation value.

@Stamoulohta
Copy link
Contributor Author

Thank you for the prompt review. I implemented the changes you proposed and merged with Dev.

This is my first pull request and I can't find a way to change the base branch from my end.. If you want me I can make a new one to the correct branch.

Thanks again,
George

@syockit syockit mentioned this pull request Feb 22, 2020
@rom1v
Copy link
Collaborator

rom1v commented Feb 22, 2020

merged with Dev

😱 don't merge branches in a PR/patchset :) You should rebase on dev instead.

If you want, just push force your last commit before the merge (keep this PR on master), I'll rebase on dev when merging the PR.

(I will try to find time to review the changes next week)

@Stamoulohta
Copy link
Contributor Author

don't merge branches in a PR/patchset :) You should rebase on dev instead.

If you want, just push force your last commit before the merge (keep this PR on master), I'll rebase on dev when merging the PR.

Sorry about that. I hope things are fixed now.

Looking forward to your review.

Thanks,
George

@rom1v
Copy link
Collaborator

rom1v commented Feb 24, 2020

Hi,

I had some time tonight \o/

I squashed your 2 last commits, and applied the following changes:

  • The requested video orientation is now interpreted counterclockwise (it's more natural IMO). For example, "natural" landscape is --lock-video-orientation 1 instead of 3.
  • The rotation is not applied from onRotationChanged() (it was racy, this method is called from another thread). Instead, it is computed when necessary from the device rotation (stored in ScreenInfo) and the locked video orientation.
  • The implementation of ScreenInfo.withRotation() has been fixed in case the device is rotated directly by 180°.
  • Some other minor changes.

My commit on master is here: a58de78.

I would appreciate if you could review my changes and test :)

Here is the range-diff (diff of diffs between your 2 commits squashed and my commit which includes my changes, it can be hard to read but it gives the full changes):

range-diff
1:  46ec407 ! 1:  a58de78 Client orientation option
    @@ Metadata
     Author: George Stamoulis <[email protected]>
     
      ## Commit message ##
    -    Client orientation option
    +    Add option to lock video orientation
    +
    +    Signed-off-by: Romain Vimont <[email protected]>
     
      ## README.md ##
    -@@ README.md: The initial window position and size may be specified:
    - scrcpy --window-x 100 --window-y 100 --window-width 800 --window-height 600
    - ```
    +@@ README.md: scrcpy --crop 1224:1440:0:0   # 1224x1440 at offset (0,0)
    + If `--max-size` is also specified, resizing is applied after cropping.
    + 
      
    -+#### Lock orientation
    ++#### Lock video orientation
    ++
     +
    -+Keep the window in landscape orientation:
    ++To lock the orientation of the mirroring:
     +
     +```bash
    -+scrcpy --orientation 3
    ++scrcpy --lock-video-orientation 0   # natural orientation
    ++scrcpy --lock-video-orientation 1   # 90° counterclockwise
    ++scrcpy --lock-video-orientation 2   # 180°
    ++scrcpy --lock-video-orientation 3   # 90° clockwise
     +```
     +
    - #### Borderless
    ++This affects recording orientation.
    ++
    ++
    + ### Recording
      
    - To disable window decorations:
    + It is possible to record the screen while mirroring:
     
      ## app/meson.build ##
     @@ app/meson.build: conf.set('DEFAULT_LOCAL_PORT', '27183')
    @@ app/meson.build: conf.set('DEFAULT_LOCAL_PORT', '27183')
      
     +# the default video orientation
     +# natural device orientation is 0 and each increment adds 90 degrees
    ++# counterclockwise
     +# overridden by option --lock-video-orientation
     +conf.set('DEFAULT_LOCK_VIDEO_ORIENTATION', '-1')  # -1: unlocked
     +
    @@ app/meson.build: conf.set('DEFAULT_LOCAL_PORT', '27183')
      conf.set('DEFAULT_BIT_RATE', '8000000')  # 8Mbps
     
      ## app/scrcpy.1 ##
    -@@ app/scrcpy.1: Limit both the width and height of the video to \fIvalue\fR. The other dimension
    - 
    - Default is 0 (unlimited).
    +@@ app/scrcpy.1: Start in fullscreen.
    + .B \-h, \-\-help
    + Print this help.
      
     +.TP
     +.BI "\-\-lock\-video\-orientation " value
    -+Lock video orientation to \fIvalue\fR. Values are integers in the range [0..3]. Natural device orientation is 0 and each increment adds 90 degrees.
    ++Lock video orientation to \fIvalue\fR. Values are integers in the range [-1..3]. Natural device orientation is 0 and each increment adds 90 degrees counterclockwise.
     +
     +Default is -1 (unlocked).
     +
      .TP
    - .B \-n, \-\-no\-control
    - Disable device control (mirror the device in read\-only).
    + .BI "\-\-max\-fps " value
    + Limit the framerate of screen capture (only supported on devices with Android >= 10).
     
      ## app/src/cli.c ##
     @@ app/src/cli.c: scrcpy_print_usage(const char *arg0) {
    -         "        is preserved.\n"
    -         "        Default is %d%s.\n"
    +         "    -h, --help\n"
    +         "        Print this help.\n"
              "\n"
     +        "    --lock-video-orientation value\n"
     +        "        Lock video 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"
    ++        "        range [-1..3]. Natural device orientation is 0 and each\n"
    ++        "        increment adds 90 degrees counterclockwise.\n"
     +        "        Default is %d%s.\n"
     +        "\n"
    -         "    -n, --no-control\n"
    -         "        Disable device control (mirror the device in read-only).\n"
    -         "\n"
    +         "    --max-fps value\n"
    +         "        Limit the frame rate of screen capture (only supported on\n"
    +         "        devices with Android >= 10).\n"
     @@ app/src/cli.c: scrcpy_print_usage(const char *arg0) {
              arg0,
              DEFAULT_BIT_RATE,
    @@ app/src/cli.c: parse_max_fps(const char *s, uint16_t *max_fps) {
     +parse_lock_video_orientation(const char *s, int8_t *lock_video_orientation) {
     +    long value;
     +    bool ok = parse_integer_arg(s, &value, false, -1, 3,
    -+                                "lock_video_orientation");
    ++                                "lock video orientation");
     +    if (!ok) {
     +        return false;
     +    }
    @@ app/src/cli.c: guess_record_format(const char *filename) {
     +        {"crop",                   required_argument, NULL, OPT_CROP},
     +        {"fullscreen",             no_argument,       NULL, 'f'},
     +        {"help",                   no_argument,       NULL, 'h'},
    ++        {"lock-video-orientation", required_argument, NULL,
    ++                                                  OPT_LOCK_VIDEO_ORIENTATION},
     +        {"max-fps",                required_argument, NULL, OPT_MAX_FPS},
     +        {"max-size",               required_argument, NULL, 'm'},
    -+        {"lock-video-orientation", required_argument, NULL, OPT_LOCK_VIDEO_ORIENTATION},
     +        {"no-control",             no_argument,       NULL, 'n'},
     +        {"no-display",             no_argument,       NULL, 'N'},
     +        {"port",                   required_argument, NULL, 'p'},
    @@ app/src/cli.c: guess_record_format(const char *filename) {
     +        {"record",                 required_argument, NULL, 'r'},
     +        {"record-format",          required_argument, NULL, OPT_RECORD_FORMAT},
     +        {"render-expired-frames",  no_argument,       NULL,
    -+                                                      OPT_RENDER_EXPIRED_FRAMES},
    ++                                                  OPT_RENDER_EXPIRED_FRAMES},
     +        {"serial",                 required_argument, NULL, 's'},
     +        {"show-touches",           no_argument,       NULL, 't'},
     +        {"turn-screen-off",        no_argument,       NULL, 'S'},
    @@ app/src/cli.c: guess_record_format(const char *filename) {
     +        {"window-width",           required_argument, NULL, OPT_WINDOW_WIDTH},
     +        {"window-height",          required_argument, NULL, OPT_WINDOW_HEIGHT},
     +        {"window-borderless",      no_argument,       NULL,
    -+                                                      OPT_WINDOW_BORDERLESS},
    ++                                                  OPT_WINDOW_BORDERLESS},
     +        {NULL,                     0,                 NULL, 0  },
          };
      
    @@ app/tests/test_cli.c: static void test_options(void) {
          assert(!strcmp(opts->push_target, "/sdcard/Movies"));
          assert(!strcmp(opts->record_filename, "file"));
     
    - ## server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java ##
    -@@ server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java: public class ControlMessageReader {
    -     private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
    -     private final byte[] textBuffer = new byte[CLIPBOARD_TEXT_MAX_LENGTH];
    + ## server/src/main/java/com/genymobile/scrcpy/Device.java ##
    +@@ server/src/main/java/com/genymobile/scrcpy/Device.java: public final class Device {
    + 
    +     private final ServiceManager serviceManager = new ServiceManager();
      
     +
    -     public ControlMessageReader() {
    -         // invariant: the buffer is always in "get" mode
    -         buffer.limit(0);
    -
    - ## server/src/main/java/com/genymobile/scrcpy/Device.java ##
    ++    private final int lockedVideoOrientation;
    +     private ScreenInfo screenInfo;
    +     private RotationListener rotationListener;
    + 
    +     public Device(Options options) {
    ++        lockedVideoOrientation = options.getLockedVideoOrientation();
    +         screenInfo = computeScreenInfo(options.getCrop(), options.getMaxSize());
    +         registerRotationWatcher(new IRotationWatcher.Stub() {
    +             @Override
     @@ server/src/main/java/com/genymobile/scrcpy/Device.java: public final class Device {
      
          private ScreenInfo computeScreenInfo(Rect crop, int maxSize) {
    @@ server/src/main/java/com/genymobile/scrcpy/Device.java: public final class Devic
              Rect contentRect = new Rect(0, 0, deviceSize.getWidth(), deviceSize.getHeight());
              if (crop != null) {
     -            if (rotated) {
    -+            if (rotation % 2 != 0 ) { // 180° preserve dimensions
    ++            if (rotation % 2 != 0) { // 180s preserve dimensions
                      // the crop (provided by the user) is expressed in the natural orientation
                      crop = flipRect(crop);
                  }
    @@ server/src/main/java/com/genymobile/scrcpy/Device.java: public final class Devic
      
          private static String formatCrop(Rect rect) {
     @@ server/src/main/java/com/genymobile/scrcpy/Device.java: public final class Device {
    +         return new Size(w, h);
    +     }
    + 
    ++    /**
    ++     * Return the rotation to apply to the device rotation to get the requested locked video orientation
    ++     *
    ++     * @param deviceRotation the device rotation
    ++     * @return the rotation offset
    ++     */
    ++    public int getVideoRotation(int deviceRotation) {
    ++        if (lockedVideoOrientation == -1) {
    ++            // no offset
    ++            return 0;
    ++        }
    ++        return (deviceRotation + 4 - lockedVideoOrientation) % 4;
    ++    }
    ++
    ++    /**
    ++     * Return the rotation to apply to the requested locked video orientation to get the device rotation
    ++     * @param deviceRotation the device rotation
    ++     * @return the (reverse) rotation offset
    ++     */
    ++    private int getReverseVideoRotation(int deviceRotation) {
    ++        if (lockedVideoOrientation == -1) {
    ++            // no offset
    ++            return 0;
    ++        }
    ++        return (lockedVideoOrientation + 4 - deviceRotation) % 4;
    ++    }
    ++
    +     public Point getPhysicalPoint(Position position) {
    +         // it hides the field on purpose, to read it with a lock
              @SuppressWarnings("checkstyle:HiddenField")
              ScreenInfo screenInfo = getScreenInfo(); // read with synchronization
              Size videoSize = screenInfo.getVideoSize();
    -+        position = position.rotate(ScreenEncoder.rotationOffset);
    -         Size clientVideoSize = position.getScreenSize();
    +-        Size clientVideoSize = position.getScreenSize();
    ++
    ++        int deviceRotation = screenInfo.getRotation();
    ++        int reverseVideoRotation = getReverseVideoRotation(deviceRotation);
    ++        // reverse the video rotation to apply the events
    ++        Position devicePosition = position.rotate(reverseVideoRotation);
    ++
    ++        Size clientVideoSize = devicePosition.getScreenSize();
              if (!videoSize.equals(clientVideoSize)) {
                  // The client sends a click relative to a video with wrong dimensions,
    -@@ server/src/main/java/com/genymobile/scrcpy/Device.java: public final class Device {
    +             // the device may have been rotated since the event was generated, so ignore the event
    +             return null;
              }
              Rect contentRect = screenInfo.getContentRect();
    -         Point point = position.getPoint();
    +-        Point point = position.getPoint();
     -        int scaledX = contentRect.left + point.getX() * contentRect.width() / videoSize.getWidth();
     -        int scaledY = contentRect.top + point.getY() * contentRect.height() / videoSize.getHeight();
     -        return new Point(scaledX, scaledY);
    ++        Point point = devicePosition.getPoint();
     +        int convertedX = contentRect.left + point.getX() * contentRect.width() / videoSize.getWidth();
     +        int convertedY = contentRect.top + point.getY() * contentRect.height() / videoSize.getHeight();
     +        return new Point(convertedX, convertedY);
          }
      
          public static String getDeviceName() {
    -@@ server/src/main/java/com/genymobile/scrcpy/Device.java: public final class Device {
    -         }
    -     }
    - 
    -+    @SuppressWarnings("SuspiciousNameCombination")
    -     static Rect flipRect(Rect crop) {
    -         return new Rect(crop.top, crop.left, crop.bottom, crop.right);
    -     }
     
      ## server/src/main/java/com/genymobile/scrcpy/Options.java ##
     @@ server/src/main/java/com/genymobile/scrcpy/Options.java: public class Options {
    @@ server/src/main/java/com/genymobile/scrcpy/Position.java: public class Position
          }
      
     +    public Position rotate(int rotation) {
    -+        Position position;
     +        switch (rotation) {
     +            case 1:
    -+                position = new Position(new Point(point.getY(), screenSize.getWidth() - point.getX()), screenSize.rotate());
    -+                break;
    ++                return new Position(new Point(screenSize.getHeight() - point.getY(), point.getX()), screenSize.rotate());
     +            case 2:
    -+                position = new Position(new Point(screenSize.getWidth() - point.getX(), screenSize.getHeight() - point.getY()), screenSize);
    -+                break;
    ++                return new Position(new Point(screenSize.getWidth() - point.getX(), screenSize.getHeight() - point.getY()), screenSize);
     +            case 3:
    -+                position = new Position(new Point(screenSize.getHeight() - point.getY(), point.getX()), screenSize.rotate());
    -+                break;
    ++                return new Position(new Point(point.getY(), screenSize.getWidth() - point.getX()), screenSize.rotate());
     +            default:
    -+                position = this;
    ++                return this;
     +        }
    -+        return position;
     +    }
     +
          @Override
    @@ server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java: public class Scre
          private long ptsOrigin;
      
     -    public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int iFrameInterval) {
    -+    static int rotationOffset;
    -+
     +    public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, int lockedVideoOrientation, int iFrameInterval) {
              this.sendFrameMeta = sendFrameMeta;
              this.bitRate = bitRate;
    @@ server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java: public class Scre
          }
      
          @Override
    -     public void onRotationChanged(int rotation) {
    -+        setRotationOffset(rotation);
    -         rotationChanged.set(true);
    -     }
    - 
    -@@ server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java: public class ScreenEncoder implements Device.RotationListener {
    - 
    -         MediaFormat format = createFormat(bitRate, maxFps, iFrameInterval);
    -         device.setRotationListener(this);
    -+        setRotationOffset(device.getScreenInfo().getRotation());
    -         boolean alive;
    -         try {
    -             do {
     @@ server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java: public class ScreenEncoder implements Device.RotationListener {
    -                 IBinder display = createDisplay();
    -                 Rect contentRect = device.getScreenInfo().getContentRect();
    -                 Rect videoRect = device.getScreenInfo().getVideoSize().toRect();
    +                 ScreenInfo screenInfo = device.getScreenInfo();
    +                 Rect contentRect = screenInfo.getContentRect();
    +                 Rect videoRect = screenInfo.getVideoSize().toRect();
     -                setSize(format, videoRect.width(), videoRect.height());
    -+                setSize(format, rotationOffset, videoRect.width(), videoRect.height());
    ++                int videoRotation = device.getVideoRotation(screenInfo.getRotation());
    ++                setSize(format, videoRotation, videoRect.width(), videoRect.height());
                      configure(codec, format);
                      Surface surface = codec.createInputSurface();
     -                setDisplaySurface(display, surface, contentRect, videoRect);
    -+                setDisplaySurface(display, surface, rotationOffset, contentRect, videoRect);
    ++                setDisplaySurface(display, surface, videoRotation, contentRect, videoRect);
                      codec.start();
                      try {
                          alive = encode(codec, fd);
    -@@ server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java: public class ScreenEncoder implements Device.RotationListener {
    -         IO.writeFully(fd, headerBuffer);
    -     }
    - 
    -+    private void setRotationOffset(int rotation) {
    -+        if (lockedVideoOrientation != -1) { // user has requested orientation
    -+            rotationOffset = (rotation + lockedVideoOrientation) % 4;
    -+        }
    -+    }
    -+
    -     private static MediaCodec createCodec() throws IOException {
    -         return MediaCodec.createEncoderByType("video/avc");
    -     }
     @@ server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java: public class ScreenEncoder implements Device.RotationListener {
              codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
          }
    @@ server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java: public final class S
     -    public ScreenInfo withRotation(int rotation) {
     -        boolean newRotated = (rotation & 1) != 0;
     -        if (rotated == newRotated) {
    --            return this;
    --        }
    --        return new ScreenInfo(Device.flipRect(contentRect), videoSize.rotate(), newRotated);
     +    public int getRotation() {
     +        return rotation;
     +    }
     +
     +    public ScreenInfo withRotation(int newRotation) {
    -+        return new ScreenInfo(Device.flipRect(contentRect), videoSize.rotate(), newRotation);
    ++        if (newRotation == rotation) {
    +             return this;
    +         }
    +-        return new ScreenInfo(Device.flipRect(contentRect), videoSize.rotate(), newRotated);
    ++        // true if changed between portrait and landscape
    ++        boolean orientationChanged = (rotation + newRotation) % 2 != 0;
    ++        Rect newContentRect;
    ++        Size newVideoSize;
    ++        if (orientationChanged) {
    ++            newContentRect = Device.flipRect(contentRect);
    ++            newVideoSize = videoSize.rotate();
    ++        } else {
    ++            newContentRect = contentRect;
    ++            newVideoSize = videoSize;
    ++        }
    ++        return new ScreenInfo(newContentRect, newVideoSize, newRotation);
          }
      }
     
    @@ server/src/main/java/com/genymobile/scrcpy/Server.java: public final class Serve
              boolean tunnelForward = options.isTunnelForward();
              try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
     -            ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps());
    -+            ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), options.getLockedVideoOrientation());
    ++            ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(),
    ++                    options.getLockedVideoOrientation());
      
                  if (options.getControl()) {
                      Controller controller = new Controller(device, connection);

@rom1v rom1v mentioned this pull request Feb 25, 2020
@Stamoulohta
Copy link
Contributor Author

Thank you for the review. I am ok with all changes and everything works for me. Only thing is that you should also swap lines 201 and 202 at cli.c so the default values are reported correctly.

Thank you so much,
George

@rom1v
Copy link
Collaborator

rom1v commented Feb 27, 2020

I am ok with all changes and everything works for me.

Cool, thank you for your review and tests 👍

Only thing is that you should also swap lines 201 and 202 at cli.c so the default values are reported correctly.

Good catch!

I will rebase on dev, I need to test on tablet too, then I'll merge it. Maybe next week.

Thanks!

rom1v pushed a commit that referenced this pull request Feb 27, 2020
@rom1v
Copy link
Collaborator

rom1v commented Feb 27, 2020

I tested on tablet, it works 🎉

I rebased and merged on dev branch: 1982bc4

Thank you for your contribution. 👍

@rom1v rom1v closed this Feb 27, 2020
This was referenced Feb 27, 2020
Copy link

@LEENO82 LEENO82 left a comment

Choose a reason for hiding this comment

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

Thank you for the review. I am ok with all changes and everything works for me. Only thing is that you should also swap lines 201 and 202 at cli.c so the default values are reported correctly.

Thank you so much,
George

Thank to you bro, you mean i can fix my rotation problem? How to fix it?

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

@rom1v
Copy link
Collaborator

rom1v commented Mar 5, 2020

@Stamoulohta I noticed that the initial video size was incorrect if the video orientation was locked in an orientation different from the device.

Concretely, the client initialized the window to 1080x1920, but immediately (after the first frame) it reset to 1920x1080.

I refactored a bit and implemented changes to send the correct initial size: locked. I would like you to review/test if you can, before I merge it into dev 😉

Thank you.

@Stamoulohta
Copy link
Contributor Author

@rom1v Surely! I will have some time later today I think. I'll get back to you as soon as possible.

@rom1v
Copy link
Collaborator

rom1v commented Mar 14, 2020

Did you find some time to test? :)

@Stamoulohta
Copy link
Contributor Author

Did you find some time to test? :)

Unfortunately not :(

I haven't forgot about it but It's been a bit hectic these last days here in Greece. I hope this week we will reach a relative normality and we'll be able to go on with our lives.

Sorry about that,
George

@rom1v
Copy link
Collaborator

rom1v commented Mar 14, 2020

No problem, I understand.

I merged it into dev. If you encounter any problem when you test, we'll fix them :)

@Stamoulohta
Copy link
Contributor Author

Great!
Thanks :)

rom1v added a commit that referenced this pull request Apr 12, 2020
During PR #1151, this field has been moved to ScreenInfo, but has not
been removed from ScreenEncoder.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants