diff --git a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java index 5f4e1803f9..cb9ad70e32 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java @@ -31,6 +31,7 @@ public class ScreenCapture extends SurfaceCapture { private final int displayId; private int maxSize; private final Rect crop; + private Rect orientedCrop; private Orientation.Lock captureOrientationLock; private Orientation captureOrientation; private final float angle; @@ -43,7 +44,8 @@ public class ScreenCapture extends SurfaceCapture { private IBinder display; private VirtualDisplay virtualDisplay; - private AffineMatrix transform; + private AffineMatrix eventTransform; + private AffineMatrix glTransform; private OpenGLRunner glRunner; public ScreenCapture(VirtualDisplayListener vdListener, Options options) { @@ -85,19 +87,29 @@ public void prepare() throws ConfigurationException { captureOrientation = Orientation.fromRotation(displayInfo.getRotation()); } - VideoFilter filter = new VideoFilter(displaySize); + VideoFilter eventFilter = new VideoFilter(displaySize); if (crop != null) { boolean transposed = (displayInfo.getRotation() % 2) != 0; - filter.addCrop(crop, transposed); + eventFilter.addCrop(crop, transposed); + orientedCrop = transposed ? new Rect(crop.top, crop.left, crop.bottom, crop.right) : new Rect(crop); + } else { + orientedCrop = null; } boolean locked = captureOrientationLock != Orientation.Lock.Unlocked; - filter.addOrientation(displayInfo.getRotation(), locked, captureOrientation); - filter.addAngle(angle); + eventFilter.addOrientation(displayInfo.getRotation(), locked, captureOrientation); + eventFilter.addAngle(angle); + + eventTransform = eventFilter.getInverseTransform(); - transform = filter.getInverseTransform(); - videoSize = filter.getOutputSize().limit(maxSize).round8(); + Size glInputSize = orientedCrop != null ? new Size(orientedCrop.width(), orientedCrop.height()) : displaySize; + VideoFilter glFilter = new VideoFilter(glInputSize); + glFilter.addOrientation(displayInfo.getRotation(), locked, captureOrientation); + glFilter.addAngle(angle); + + glTransform = glFilter.getInverseTransform(); + videoSize = glFilter.getOutputSize().limit(maxSize).round8(); } @Override @@ -112,33 +124,45 @@ public void start(Surface surface) throws IOException { } Size inputSize; - if (transform != null) { - // If there is a filter, it must receive the full display content - inputSize = displayInfo.getSize(); + if (glTransform != null) { + inputSize = orientedCrop != null ? new Size(orientedCrop.width(), orientedCrop.height()) : displayInfo.getSize(); assert glRunner == null; - OpenGLFilter glFilter = new AffineOpenGLFilter(transform); + OpenGLFilter glFilter = new AffineOpenGLFilter(glTransform); glRunner = new OpenGLRunner(glFilter); surface = glRunner.start(inputSize, videoSize, surface); } else { - // If there is no filter, the display must be rendered at target video size directly inputSize = videoSize; } - try { - virtualDisplay = ServiceManager.getDisplayManager() - .createVirtualDisplay("scrcpy", inputSize.getWidth(), inputSize.getHeight(), displayId, surface); - Ln.d("Display: using DisplayManager API"); - } catch (Exception displayManagerException) { + if (orientedCrop == null) { + try { + virtualDisplay = ServiceManager.getDisplayManager() + .createVirtualDisplay("scrcpy", inputSize.getWidth(), inputSize.getHeight(), displayId, surface); + Ln.d("Display: using DisplayManager API"); + } catch (Exception displayManagerException) { + try { + display = createDisplay(); + + Size deviceSize = displayInfo.getSize(); + int layerStack = displayInfo.getLayerStack(); + setDisplaySurface(display, surface, deviceSize.toRect(), inputSize.toRect(), layerStack); + Ln.d("Display: using SurfaceControl API"); + } catch (Exception surfaceControlException) { + Ln.e("Could not create display using DisplayManager", displayManagerException); + Ln.e("Could not create display using SurfaceControl", surfaceControlException); + throw new AssertionError("Could not create display"); + } + } + } else { try { display = createDisplay(); - Size deviceSize = displayInfo.getSize(); int layerStack = displayInfo.getLayerStack(); - setDisplaySurface(display, surface, deviceSize.toRect(), inputSize.toRect(), layerStack); + Rect displayRect = new Rect(0, 0, inputSize.getWidth(), inputSize.getHeight()); + setDisplaySurface(display, surface, orientedCrop, displayRect, layerStack); Ln.d("Display: using SurfaceControl API"); - } catch (Exception surfaceControlException) { - Ln.e("Could not create display using DisplayManager", displayManagerException); - Ln.e("Could not create display using SurfaceControl", surfaceControlException); + } catch (Exception e) { + Ln.e("Could not create display", e); throw new AssertionError("Could not create display"); } } @@ -147,13 +171,11 @@ public void start(Surface surface) throws IOException { int virtualDisplayId; PositionMapper positionMapper; if (virtualDisplay == null || displayId == 0) { - // Surface control or main display: send all events to the original display, relative to the device size Size deviceSize = displayInfo.getSize(); - positionMapper = PositionMapper.create(videoSize, transform, deviceSize); + positionMapper = PositionMapper.create(videoSize, eventTransform, deviceSize); virtualDisplayId = displayId; } else { - // The positions are relative to the virtual display, not the original display (so use inputSize, not deviceSize!) - positionMapper = PositionMapper.create(videoSize, transform, inputSize); + positionMapper = PositionMapper.create(videoSize, eventTransform, inputSize); virtualDisplayId = virtualDisplay.getDisplay().getDisplayId(); } vdListener.onNewVirtualDisplay(virtualDisplayId, positionMapper);