From 66005e88893f39dc995472def203f0c4bc9aa2b3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 10 Apr 2026 22:48:44 +0200 Subject: [PATCH] Respect encoder minimum size constraint Encoders require a minimum video size. Use video capabilities to constrain a given size by the declared minimum size of the selected encoder. Contrary to the maximum size constraints (but like the alignment constraint), use the same value in both directions for simplicity and so that rotating the device does not change the shape of the video. PR #6766 --- .../java/com/genymobile/scrcpy/device/Size.java | 9 +++++++++ .../genymobile/scrcpy/video/SurfaceEncoder.java | 6 +++++- .../genymobile/scrcpy/video/VideoConstraints.java | 15 ++++++++++++++- .../com/genymobile/scrcpy/device/SizeTest.java | 15 ++++++++++++++- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/device/Size.java b/server/src/main/java/com/genymobile/scrcpy/device/Size.java index a62af0af..36161758 100644 --- a/server/src/main/java/com/genymobile/scrcpy/device/Size.java +++ b/server/src/main/java/com/genymobile/scrcpy/device/Size.java @@ -72,6 +72,15 @@ public final class Size { assert w <= maxWidth : "The width cannot exceed maxWidth"; assert h <= maxHeight : "The height cannot exceed maxHeight"; + // Minimum codec size must be respected (regardless of requested maxSize) + int minCodecSize = constraints.getMinCodecSize(); + if (w < minCodecSize) { + w = minCodecSize; + } + if (h < minCodecSize) { + h = minCodecSize; + } + return new Size(w, h); } diff --git a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java index 62e379bd..bb6af319 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java @@ -73,7 +73,11 @@ public class SurfaceEncoder implements AsyncProcessor { int maxPortraitWidth = caps.getSupportedWidthsFor(maxPortraitHeight).getUpper(); Size maxPortraitSize = new Size(maxPortraitWidth, maxPortraitHeight); - return new VideoConstraints(maxSize, alignment, maxLandscapeSize, maxPortraitSize); + int minWidth = caps.getSupportedWidths().getLower(); + int minHeight = caps.getSupportedHeights().getLower(); + int minSize = Math.max(minWidth, minHeight); + + return new VideoConstraints(maxSize, alignment, maxLandscapeSize, maxPortraitSize, minSize); } private void streamCapture() throws IOException, ConfigurationException { diff --git a/server/src/main/java/com/genymobile/scrcpy/video/VideoConstraints.java b/server/src/main/java/com/genymobile/scrcpy/video/VideoConstraints.java index 748f27be..f688efcb 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/VideoConstraints.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/VideoConstraints.java @@ -7,8 +7,9 @@ public class VideoConstraints { private final int alignment; private final Size maxCodecLandscapeSize; private final Size maxCodecPortraitSize; + private final int minCodecSize; - public VideoConstraints(int maxSize, int alignment, Size maxCodecLandscapeSize, Size maxCodecPortraitSize) { + public VideoConstraints(int maxSize, int alignment, Size maxCodecLandscapeSize, Size maxCodecPortraitSize, int minCodecSize) { assert maxSize >= 0 : "Max size must not be negative"; this.maxSize = maxSize; @@ -21,6 +22,9 @@ public class VideoConstraints { assert maxCodecPortraitSize != null; this.maxCodecPortraitSize = maxCodecPortraitSize; + + assert minCodecSize >= 0; + this.minCodecSize = minCodecSize; } /** @@ -60,4 +64,13 @@ public class VideoConstraints { public Size getMaxCodecPortraitSize() { return maxCodecPortraitSize; } + + /** + * Return the min size supported by the codec. + * + * @return the min size + */ + public int getMinCodecSize() { + return minCodecSize; + } } diff --git a/server/src/test/java/com/genymobile/scrcpy/device/SizeTest.java b/server/src/test/java/com/genymobile/scrcpy/device/SizeTest.java index 14834b8b..954fcf9c 100644 --- a/server/src/test/java/com/genymobile/scrcpy/device/SizeTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/device/SizeTest.java @@ -13,7 +13,11 @@ public class SizeTest { } private VideoConstraints createVideoConstraints(int maxSize, int alignment, Size maxCodecSize) { - return new VideoConstraints(maxSize, alignment, maxCodecSize, maxCodecSize); + return new VideoConstraints(maxSize, alignment, maxCodecSize, maxCodecSize, 0); + } + + private VideoConstraints createVideoConstraints(int maxSize, int alignment, Size maxCodecSize, int minCodecSize) { + return new VideoConstraints(maxSize, alignment, maxCodecSize, maxCodecSize, minCodecSize); } @Test @@ -59,4 +63,13 @@ public class SizeTest { Assert.assertEquals(new Size(400, 536 * 400 / 512), size.constrain(createVideoConstraints(500, 1, maxCodecSize))); Assert.assertEquals(new Size(512 * 410 / 536, 410), size.constrain(createVideoConstraints(410, 1, maxCodecSize))); } + + @Test + public void testConstrainMinCodecSize() { + Size maxCodecSize = new Size(1024, 1024); + + Size size = new Size(800, 600); + Assert.assertEquals(new Size(800, 600), size.constrain(createVideoConstraints(800, 1, maxCodecSize, 512))); + Assert.assertEquals(new Size(512, 512), size.constrain(createVideoConstraints(400, 1, maxCodecSize, 512))); + } }