diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index eedbd504..3f4c649d 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -56,7 +56,6 @@ _scrcpy() { --no-audio-playback --no-cleanup --no-clipboard-autosync - --no-downsize-on-error --no-key-repeat --no-mipmaps --no-mouse-hover diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index 4bf3f9dc..e106f8a3 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -62,7 +62,6 @@ arguments=( '--no-audio-playback[Disable audio playback]' '--no-cleanup[Disable device cleanup actions on exit]' '--no-clipboard-autosync[Disable automatic clipboard synchronization]' - '--no-downsize-on-error[Disable lowering definition on MediaCodec error]' '--no-key-repeat[Do not forward repeated key events when a key is held down]' '--no-mipmaps[Disable the generation of mipmaps]' '--no-mouse-hover[Do not forward mouse hover events]' diff --git a/app/scrcpy.1 b/app/scrcpy.1 index e2a65bae..b6423c28 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -384,12 +384,6 @@ By default, scrcpy automatically synchronizes the computer clipboard to the devi This option disables this automatic synchronization. -.TP -.B \-\-no\-downsize\-on\-error -By default, on MediaCodec error, scrcpy automatically tries again with a lower definition. - -This option disables this behavior. - .TP .B \-\-no\-key\-repeat Do not forward repeated key events when a key is held down. diff --git a/app/src/cli.c b/app/src/cli.c index 8c5e09c9..e4076529 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -51,7 +51,6 @@ enum { OPT_NO_CLIPBOARD_AUTOSYNC, OPT_TCPIP, OPT_RAW_KEY_EVENTS, - OPT_NO_DOWNSIZE_ON_ERROR, OPT_OTG, OPT_NO_CLEANUP, OPT_PRINT_FPS, @@ -611,13 +610,6 @@ static const struct sc_option options[] = { "it changes.\n" "This option disables this automatic synchronization." }, - { - .longopt_id = OPT_NO_DOWNSIZE_ON_ERROR, - .longopt = "no-downsize-on-error", - .text = "By default, on MediaCodec error, scrcpy automatically tries " - "again with a lower definition.\n" - "This option disables this behavior.", - }, { .longopt_id = OPT_NO_KEY_REPEAT, .longopt = "no-key-repeat", @@ -2581,9 +2573,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], opts->tcpip = true; opts->tcpip_dst = optarg; break; - case OPT_NO_DOWNSIZE_ON_ERROR: - opts->downsize_on_error = false; - break; case OPT_NO_VIDEO: opts->video = false; break; @@ -2856,16 +2845,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], } #ifdef HAVE_V4L2 - if (v4l2) { - if (!opts->video) { - LOGE("V4L2 sink requires video capture, but --no-video was set."); - return false; - } - - // V4L2 could not handle size change. - // Do not log because downsizing on error is the default behavior, - // not an explicit request from the user. - opts->downsize_on_error = false; + if (v4l2 && !opts->video) { + LOGE("V4L2 sink requires video capture, but --no-video was set."); + return false; } if (opts->v4l2_buffer && !opts->v4l2_device) { diff --git a/app/src/options.c b/app/src/options.c index 9ef1bce7..10ad471f 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -94,7 +94,6 @@ const struct scrcpy_options scrcpy_options_default = { .legacy_paste = false, .power_off_on_close = false, .clipboard_autosync = true, - .downsize_on_error = true, .tcpip = false, .tcpip_dst = NULL, .select_tcpip = false, diff --git a/app/src/options.h b/app/src/options.h index abd09d61..752d5870 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -304,7 +304,6 @@ struct scrcpy_options { bool legacy_paste; bool power_off_on_close; bool clipboard_autosync; - bool downsize_on_error; bool tcpip; const char *tcpip_dst; bool select_usb; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 8bb428c9..d067b372 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -455,7 +455,6 @@ scrcpy(struct scrcpy_options *options) { .force_adb_forward = options->force_adb_forward, .power_off_on_close = options->power_off_on_close, .clipboard_autosync = options->clipboard_autosync, - .downsize_on_error = options->downsize_on_error, .tcpip = options->tcpip, .tcpip_dst = options->tcpip_dst, .cleanup = options->cleanup, diff --git a/app/src/server.c b/app/src/server.c index df6e2b1f..7a33057a 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -401,10 +401,6 @@ execute_server(struct sc_server *server, // By default, clipboard_autosync is true ADD_PARAM("clipboard_autosync=false"); } - if (!params->downsize_on_error) { - // By default, downsize_on_error is true - ADD_PARAM("downsize_on_error=false"); - } if (!params->cleanup) { // By default, cleanup is true ADD_PARAM("cleanup=false"); diff --git a/app/src/server.h b/app/src/server.h index 03517233..ebb1f137 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -61,7 +61,6 @@ struct sc_server_params { bool force_adb_forward; bool power_off_on_close; bool clipboard_autosync; - bool downsize_on_error; bool tcpip; const char *tcpip_dst; bool select_usb; diff --git a/doc/video.md b/doc/video.md index 8cea0373..7b1e4c03 100644 --- a/doc/video.md +++ b/doc/video.md @@ -24,9 +24,6 @@ scrcpy -m 1024 # short version The other dimension is computed so that the Android device aspect ratio is preserved. That way, a device in 1920×1080 will be mirrored at 1024×576. -If encoding fails, scrcpy automatically tries again with a lower definition -(unless `--no-downsize-on-error` is enabled). - For camera mirroring, the `--max-size` value is used to select the camera source size instead (among the available resolutions). diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index d2c324f6..aba8251e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -60,7 +60,6 @@ public class Options { private String audioEncoder; private boolean powerOffScreenOnClose; private boolean clipboardAutosync = true; - private boolean downsizeOnError = true; private boolean cleanup = true; private boolean powerOn = true; @@ -231,10 +230,6 @@ public class Options { return clipboardAutosync; } - public boolean getDownsizeOnError() { - return downsizeOnError; - } - public boolean getCleanup() { return cleanup; } @@ -443,9 +438,6 @@ public class Options { case "clipboard_autosync": options.clipboardAutosync = Boolean.parseBoolean(value); break; - case "downsize_on_error": - options.downsizeOnError = Boolean.parseBoolean(value); - break; case "cleanup": options.cleanup = Boolean.parseBoolean(value); break; 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 4c85b185..b9fbd9b7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java @@ -59,7 +59,7 @@ public class CameraCapture extends SurfaceCapture { private final String explicitCameraId; private final CameraFacing cameraFacing; private final Size explicitSize; - private int maxSize; + private final int maxSize; private final CameraAspectRatio aspectRatio; private final int fps; private final boolean highSpeed; @@ -374,16 +374,6 @@ public class CameraCapture extends SurfaceCapture { return videoSize; } - @Override - public boolean setMaxSize(int maxSize) { - if (explicitSize != null) { - return false; - } - - this.maxSize = maxSize; - return true; - } - @SuppressLint("MissingPermission") @TargetApi(AndroidVersions.API_31_ANDROID_12) private CameraDevice openCamera(String id) throws CameraAccessException, InterruptedException { 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 5dab6030..cc9e8c6e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java @@ -49,7 +49,7 @@ public class NewDisplayCapture extends SurfaceCapture { private Size mainDisplaySize; private int mainDisplayDpi; - private int maxSize; + private final int maxSize; private final int displayImePolicy; private final Rect crop; private final boolean captureOrientationLocked; @@ -251,12 +251,6 @@ public class NewDisplayCapture extends SurfaceCapture { return videoSize; } - @Override - public synchronized boolean setMaxSize(int newMaxSize) { - maxSize = newMaxSize; - return true; - } - private static int scaleDpi(Size initialSize, int initialDpi, Size size) { int den = initialSize.getMax(); int num = size.getMax(); 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 4586f474..81db7159 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java @@ -29,7 +29,7 @@ public class ScreenCapture extends SurfaceCapture { private final VirtualDisplayListener vdListener; private final int displayId; - private int maxSize; + private final int maxSize; private final Rect crop; private Orientation.Lock captureOrientationLock; private Orientation captureOrientation; @@ -187,12 +187,6 @@ public class ScreenCapture extends SurfaceCapture { return videoSize; } - @Override - public boolean setMaxSize(int newMaxSize) { - maxSize = newMaxSize; - return true; - } - private static IBinder createDisplay() throws Exception { // Since Android 12 (preview), secure displays could not be created with shell permissions anymore. // On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S". 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 81da337b..2b120884 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java @@ -73,13 +73,6 @@ public abstract class SurfaceCapture { */ public abstract Size getSize(); - /** - * Set the maximum capture size (set by the encoder if it does not support the current size). - * - * @param maxSize Maximum size - */ - public abstract boolean setMaxSize(int maxSize); - /** * Indicate if the capture has been closed internally. * 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 e61d4d70..b1da801d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java @@ -18,7 +18,6 @@ import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.os.Build; import android.os.Looper; -import android.os.SystemClock; import android.view.Surface; import java.io.IOException; @@ -32,22 +31,14 @@ public class SurfaceEncoder implements AsyncProcessor { private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms private static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder"; - // Keep the values in descending order - private static final int[] MAX_SIZE_FALLBACK = {2560, 1920, 1600, 1280, 1024, 800}; - private static final int MAX_CONSECUTIVE_ERRORS = 3; - private final SurfaceCapture capture; private final Streamer streamer; private final String encoderName; private final List codecOptions; private final int videoBitRate; private final float maxFps; - private final boolean downsizeOnError; private final int minSizeAlignment; - private boolean firstFrameSent; - private int consecutiveErrors; - private Thread thread; private final AtomicBoolean stopped = new AtomicBoolean(); @@ -60,7 +51,6 @@ public class SurfaceEncoder implements AsyncProcessor { this.maxFps = options.getMaxFps(); this.codecOptions = options.getVideoCodecOptions(); this.encoderName = options.getVideoEncoder(); - this.downsizeOnError = options.getDownsizeOnError(); this.minSizeAlignment = options.getMinSizeAlignment(); } @@ -121,16 +111,6 @@ public class SurfaceEncoder implements AsyncProcessor { // The capture might have been closed internally (for example if the camera is disconnected) alive = !stopped.get() && !capture.isClosed(); } - } catch (IllegalStateException | IllegalArgumentException | IOException e) { - if (IO.isBrokenPipe(e)) { - // Do not retry on broken pipe, which is expected on close because the socket is closed by the client - throw e; - } - Ln.e("Capture/encoding error: " + e.getClass().getName() + ": " + e.getMessage()); - if (!prepareRetry(size)) { - throw e; - } - alive = true; } finally { reset.setRunningMediaCodec(null); if (captureStarted) { @@ -155,54 +135,6 @@ public class SurfaceEncoder implements AsyncProcessor { } } - private boolean prepareRetry(Size currentSize) { - if (firstFrameSent) { - ++consecutiveErrors; - if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) { - // Definitively fail - return false; - } - - // Wait a bit to increase the probability that retrying will fix the problem - SystemClock.sleep(50); - return true; - } - - if (!downsizeOnError) { - // Must fail immediately - return false; - } - - // Downsizing on error is only enabled if an encoding failure occurs before the first frame (downsizing later could be surprising) - - int newMaxSize = chooseMaxSizeFallback(currentSize); - if (newMaxSize == 0) { - // Must definitively fail - return false; - } - - boolean accepted = capture.setMaxSize(newMaxSize); - if (!accepted) { - return false; - } - - // Retry with a smaller size - Ln.i("Retrying with -m" + newMaxSize + "..."); - return true; - } - - private static int chooseMaxSizeFallback(Size failedSize) { - int currentMaxSize = Math.max(failedSize.getWidth(), failedSize.getHeight()); - for (int value : MAX_SIZE_FALLBACK) { - if (value < currentMaxSize) { - // We found a smaller value to reduce the video size - return value; - } - } - // No fallback, fail definitively - return 0; - } - private void encode(MediaCodec codec, Streamer streamer) throws IOException { MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); @@ -214,14 +146,6 @@ public class SurfaceEncoder implements AsyncProcessor { // On EOS, there might be data or not, depending on bufferInfo.size if (outputBufferId >= 0 && bufferInfo.size > 0) { ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId); - - boolean isConfig = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; - if (!isConfig) { - // If this is not a config packet, then it contains a frame - firstFrameSent = true; - consecutiveErrors = 0; - } - streamer.writePacket(codecBuffer, bufferInfo); } } finally {