diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index c13e2d42..fc37dbce 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -369,7 +369,7 @@ public class Options { options.audioDup = Boolean.parseBoolean(value); break; case "max_size": - options.maxSize = Integer.parseInt(value) & ~7; // multiple of 8 + options.maxSize = Integer.parseInt(value); break; case "video_bit_rate": options.videoBitRate = Integer.parseInt(value); 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 b448273d..b684a54e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/device/Size.java +++ b/server/src/main/java/com/genymobile/scrcpy/device/Size.java @@ -31,7 +31,6 @@ public final class Size { public Size limit(int maxSize) { assert maxSize >= 0 : "Max size may not be negative"; - assert maxSize % 8 == 0 : "Max size must be a multiple of 8"; if (maxSize == 0) { // No limit @@ -55,13 +54,14 @@ public final class Size { } /** - * Round both dimensions of this size to be a multiple of 8 (as required by many encoders). + * Round both dimensions of this size to be multiples of {@code alignment}. * - * @return The current size rounded. + * @param alignment the required alignment + * @return the current size rounded */ - public Size round8() { - if (isMultipleOf8()) { - // Already a multiple of 8 + public Size round(int alignment) { + if (isMultipleOf(alignment)) { + // Already aligned return this; } @@ -69,8 +69,8 @@ public final class Size { int major = portrait ? height : width; int minor = portrait ? width : height; - major &= ~7; // round down to not exceed the initial size - minor = (minor + 4) & ~7; // round to the nearest to minimize aspect ratio distortion + major = major / alignment * alignment; // round down to not exceed the initial size + minor = (minor + (alignment / 2)) / alignment * alignment; // round to the nearest to minimize aspect ratio distortion if (minor > major) { minor = major; } @@ -80,8 +80,8 @@ public final class Size { return new Size(w, h); } - public boolean isMultipleOf8() { - return (width & 7) == 0 && (height & 7) == 0; + public boolean isMultipleOf(int alignment) { + return width % alignment == 0 && height % alignment == 0; } public Rect toRect() { diff --git a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java index 5aad8446..4c85b185 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java @@ -149,7 +149,7 @@ public class CameraCapture extends SurfaceCapture { filter.addAngle(angle); transform = filter.getInverseTransform(); - videoSize = filter.getOutputSize().limit(maxSize).round8(); + videoSize = filter.getOutputSize().limit(maxSize).round(getAlignment()); } private static String selectCamera(String explicitCameraId, CameraFacing cameraFacing) throws CameraAccessException, ConfigurationException { diff --git a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java index cecd2110..0ad14d7f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java @@ -133,12 +133,13 @@ public class NewDisplayCapture extends SurfaceCapture { filter.addOrientation(displayRotation, captureOrientationLocked, captureOrientation); filter.addAngle(angle); + int alignment = getAlignment(); Size filteredSize = filter.getOutputSize(); - if (!filteredSize.isMultipleOf8() || (maxSize != 0 && filteredSize.getMax() > maxSize)) { + if (!filteredSize.isMultipleOf(alignment) || (maxSize != 0 && filteredSize.getMax() > maxSize)) { if (maxSize != 0) { filteredSize = filteredSize.limit(maxSize); } - filteredSize = filteredSize.round8(); + filteredSize = filteredSize.round(alignment); filter.addResize(filteredSize); } 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 085ff2e5..4586f474 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java @@ -97,7 +97,7 @@ public class ScreenCapture extends SurfaceCapture { filter.addAngle(angle); transform = filter.getInverseTransform(); - videoSize = filter.getOutputSize().limit(maxSize).round8(); + videoSize = filter.getOutputSize().limit(maxSize).round(getAlignment()); } @Override diff --git a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java index 3144dc51..81da337b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java @@ -17,6 +17,7 @@ public abstract class SurfaceCapture { } private CaptureListener listener; + private int alignment; /** * Notify the listener that the capture has been invalidated (for example, because its size changed, or due to a manual user request). @@ -28,8 +29,9 @@ public abstract class SurfaceCapture { /** * Called once before the first capture starts. */ - public final void init(CaptureListener listener) throws ConfigurationException, IOException { + public final void init(CaptureListener listener, int alignment) throws ConfigurationException, IOException { this.listener = listener; + this.alignment = alignment; init(); } @@ -86,4 +88,15 @@ public abstract class SurfaceCapture { public boolean isClosed() { return false; } + + /** + * Return the video alignment + *

+ * This a power-of-2 value that the video width and height must be multiples of. + * + * @return the video alignment + */ + protected int getAlignment() { + return alignment; + } } 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 b13cb5ec..a05a3613 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java @@ -65,9 +65,15 @@ public class SurfaceEncoder implements AsyncProcessor { private void streamCapture() throws IOException, ConfigurationException { Codec codec = streamer.getCodec(); MediaCodec mediaCodec = createMediaCodec(codec, encoderName); + + MediaCodecInfo.VideoCapabilities caps = mediaCodec.getCodecInfo().getCapabilitiesForType(codec.getMimeType()) + .getVideoCapabilities(); + int alignment = caps != null ? Math.max(caps.getWidthAlignment(), caps.getHeightAlignment()) : 8; + Ln.d("Video codec size alignment requirement: " + alignment + "px"); + MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions); - capture.init(reset); + capture.init(reset, alignment); try { boolean alive;