From 162043911e1d742489bf836113fe0797191a5160 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 15 Jan 2022 22:13:50 +0100 Subject: [PATCH 01/11] Compute screen size without DisplayInfo instance Use the actual rotation and size values directly. This will allow to automatically change the maxSize value on MediaCodec error. PR #2947 --- server/src/main/java/com/genymobile/scrcpy/Device.java | 3 ++- server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index ba833a06ce..419e996e18 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -69,7 +69,8 @@ public Device(Options options) { int displayInfoFlags = displayInfo.getFlags(); - screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockVideoOrientation()); + screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), displayInfo.getSize(), options.getCrop(), options.getMaxSize(), + options.getLockVideoOrientation()); layerStack = displayInfo.getLayerStack(); SERVICE_MANAGER.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() { diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java b/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java index c27322ef40..8e5b401f1e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java @@ -80,15 +80,12 @@ public ScreenInfo withDeviceRotation(int newDeviceRotation) { return new ScreenInfo(newContentRect, newUnlockedVideoSize, newDeviceRotation, lockedVideoOrientation); } - public static ScreenInfo computeScreenInfo(DisplayInfo displayInfo, Rect crop, int maxSize, int lockedVideoOrientation) { - int rotation = displayInfo.getRotation(); - + public static ScreenInfo computeScreenInfo(int rotation, Size deviceSize, Rect crop, int maxSize, int lockedVideoOrientation) { if (lockedVideoOrientation == Device.LOCK_VIDEO_ORIENTATION_INITIAL) { // The user requested to lock the video orientation to the current orientation lockedVideoOrientation = rotation; } - Size deviceSize = displayInfo.getSize(); Rect contentRect = new Rect(0, 0, deviceSize.getWidth(), deviceSize.getHeight()); if (crop != null) { if (rotation % 2 != 0) { // 180s preserve dimensions From 723faa5dee2915086c3b07fd1bfb6d541680b042 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 15 Jan 2022 22:17:38 +0100 Subject: [PATCH 02/11] Remember Device parameters This will allow to reuse them to recreate a ScreenInfo instance in order to change the maxSize value on MediaCodec error. PR #2947 --- .../src/main/java/com/genymobile/scrcpy/Device.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index 419e996e18..591460b660 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -42,6 +42,11 @@ public interface ClipboardListener { void onClipboardTextChanged(String text); } + private final Size deviceSize; + private final Rect crop; + private final int maxSize; + private final int lockVideoOrientation; + private ScreenInfo screenInfo; private RotationListener rotationListener; private ClipboardListener clipboardListener; @@ -69,8 +74,12 @@ public Device(Options options) { int displayInfoFlags = displayInfo.getFlags(); - screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), displayInfo.getSize(), options.getCrop(), options.getMaxSize(), - options.getLockVideoOrientation()); + deviceSize = displayInfo.getSize(); + crop = options.getCrop(); + maxSize = options.getMaxSize(); + lockVideoOrientation = options.getLockVideoOrientation(); + + screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation); layerStack = displayInfo.getLayerStack(); SERVICE_MANAGER.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() { From 26b4104844fb9516be13ff1f2be34e2945090e79 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 15 Jan 2022 22:47:27 +0100 Subject: [PATCH 03/11] Downsize on error Some devices are not able to encode at the device screen definition. Instead of just failing, try with a lower definition on any MediaCodec error. PR #2947 --- .../java/com/genymobile/scrcpy/Device.java | 7 ++++- .../com/genymobile/scrcpy/ScreenEncoder.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index 591460b660..763a7fadbd 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -44,7 +44,7 @@ public interface ClipboardListener { private final Size deviceSize; private final Rect crop; - private final int maxSize; + private int maxSize; private final int lockVideoOrientation; private ScreenInfo screenInfo; @@ -133,6 +133,11 @@ public void dispatchPrimaryClipChanged() { } } + public synchronized void setMaxSize(int newMaxSize) { + maxSize = newMaxSize; + screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation); + } + public synchronized ScreenInfo getScreenInfo() { return screenInfo; } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index ce6b556a7e..ba601a2224 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -25,6 +25,9 @@ public class ScreenEncoder implements Device.RotationListener { 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 NO_PTS = -1; private final AtomicBoolean rotationChanged = new AtomicBoolean(); @@ -91,6 +94,18 @@ private void internalStreamScreen(Device device, FileDescriptor fd) throws IOExc alive = encode(codec, fd); // do not call stop() on exception, it would trigger an IllegalStateException codec.stop(); + } catch (Exception e) { + Ln.e("Encoding error: " + e.getClass().getName() + ": " + e.getMessage()); + int newMaxSize = chooseMaxSizeFallback(screenInfo.getVideoSize()); + if (newMaxSize == 0) { + // Definitively fail + throw e; + } + + // Retry with a smaller device size + Ln.i("Retrying with -m" + newMaxSize + "..."); + device.setMaxSize(newMaxSize); + alive = true; } finally { destroyDisplay(display); codec.release(); @@ -102,6 +117,18 @@ private void internalStreamScreen(Device device, FileDescriptor fd) throws IOExc } } + 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 boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException { boolean eof = false; MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); From 15bf27afdddc64c2887085e075398f71d2748d06 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 15 Jan 2022 23:01:14 +0100 Subject: [PATCH 04/11] Make auto-downsize on error optional Add --no-downsize-on-error option to disable attempts to use a lower definition on MediaCodec error. PR #2947 --- app/scrcpy.1 | 6 ++++++ app/src/cli.c | 11 +++++++++++ app/src/options.c | 1 + app/src/options.h | 1 + app/src/scrcpy.c | 1 + app/src/server.c | 4 ++++ app/src/server.h | 1 + .../src/main/java/com/genymobile/scrcpy/Options.java | 9 +++++++++ .../java/com/genymobile/scrcpy/ScreenEncoder.java | 10 +++++++++- .../src/main/java/com/genymobile/scrcpy/Server.java | 6 +++++- 10 files changed, 48 insertions(+), 2 deletions(-) diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 4d02982cbe..37431a91cb 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -140,6 +140,12 @@ 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 \-n, \-\-no\-control Disable device control (mirror the device in read\-only). diff --git a/app/src/cli.c b/app/src/cli.c index 3fcf94eb62..76feb13084 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -52,6 +52,7 @@ #define OPT_NO_CLIPBOARD_AUTOSYNC 1032 #define OPT_TCPIP 1033 #define OPT_RAW_KEY_EVENTS 1034 +#define OPT_NO_DOWNSIZE_ON_ERROR 1035 struct sc_option { char shortopt; @@ -236,6 +237,13 @@ static const struct sc_option options[] = { "is preserved.\n" "Default is 0 (unlimited).", }, + { + .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_CLIPBOARD_AUTOSYNC, .longopt = "no-clipboard-autosync", @@ -1489,6 +1497,9 @@ 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_V4L2_SINK: #ifdef HAVE_V4L2 opts->v4l2_device = optarg; diff --git a/app/src/options.c b/app/src/options.c index 7a5b71af05..b8560406fc 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -54,6 +54,7 @@ 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, }; diff --git a/app/src/options.h b/app/src/options.h index b2c696644e..99f03d4053 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -129,6 +129,7 @@ struct scrcpy_options { bool legacy_paste; bool power_off_on_close; bool clipboard_autosync; + bool downsize_on_error; bool tcpip; const char *tcpip_dst; }; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 86fdf98462..e55fef80ed 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -364,6 +364,7 @@ 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, }; diff --git a/app/src/server.c b/app/src/server.c index ea7e5377e8..b62a74f1ba 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -234,6 +234,10 @@ 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"); + } #undef ADD_PARAM #undef STRBOOL diff --git a/app/src/server.h b/app/src/server.h index 8ea20dc7a0..89cdc2f41a 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -42,6 +42,7 @@ 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; }; diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 1ac171766d..ef7d85720f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -21,6 +21,7 @@ public class Options { private String encoderName; private boolean powerOffScreenOnClose; private boolean clipboardAutosync = true; + private boolean downsizeOnError = true; public Ln.Level getLogLevel() { return logLevel; @@ -149,4 +150,12 @@ public boolean getClipboardAutosync() { public void setClipboardAutosync(boolean clipboardAutosync) { this.clipboardAutosync = clipboardAutosync; } + + public boolean getDownsizeOnError() { + return downsizeOnError; + } + + public void setDownsizeOnError(boolean downsizeOnError) { + this.downsizeOnError = downsizeOnError; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index ba601a2224..10ba9fac47 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -38,14 +38,17 @@ public class ScreenEncoder implements Device.RotationListener { private final int bitRate; private final int maxFps; private final boolean sendFrameMeta; + private final boolean downsizeOnError; private long ptsOrigin; - public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List codecOptions, String encoderName) { + public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List codecOptions, String encoderName, + boolean downsizeOnError) { this.sendFrameMeta = sendFrameMeta; this.bitRate = bitRate; this.maxFps = maxFps; this.codecOptions = codecOptions; this.encoderName = encoderName; + this.downsizeOnError = downsizeOnError; } @Override @@ -96,6 +99,11 @@ private void internalStreamScreen(Device device, FileDescriptor fd) throws IOExc codec.stop(); } catch (Exception e) { Ln.e("Encoding error: " + e.getClass().getName() + ": " + e.getMessage()); + if (!downsizeOnError) { + // Fail immediately + throw e; + } + int newMaxSize = chooseMaxSizeFallback(screenInfo.getVideoSize()); if (newMaxSize == 0) { // Definitively fail diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 4f9575ae75..21e3fd92b7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -70,7 +70,7 @@ private static void scrcpy(Options options) throws IOException { try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward, control)) { ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions, - options.getEncoderName()); + options.getEncoderName(), options.getDownsizeOnError()); Thread controllerThread = null; Thread deviceMessageSenderThread = null; @@ -237,6 +237,10 @@ private static Options createOptions(String... args) { boolean clipboardAutosync = Boolean.parseBoolean(value); options.setClipboardAutosync(clipboardAutosync); break; + case "downsize_on_error": + boolean downsizeOnError = Boolean.parseBoolean(value); + options.setDownsizeOnError(downsizeOnError); + break; default: Ln.w("Unknown server option: " + key); break; From 0ec64baad437cc11d7fe7e046adb74eb2cdd9eb7 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 15 Jan 2022 22:47:31 +0100 Subject: [PATCH 05/11] Remove MediaCodec error suggestion fix Now that scrcpy attempts with a lower definition on any MediaCodec error (or the user explicitly requests to disable auto-downsizing), the suggestion is unnecessary. PR #2947 --- .../src/main/java/com/genymobile/scrcpy/Server.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 21e3fd92b7..9d7a62e39d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -1,7 +1,6 @@ package com.genymobile.scrcpy; import android.graphics.Rect; -import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.os.BatteryManager; import android.os.Build; @@ -267,16 +266,6 @@ private static Rect parseCrop(String crop) { } private static void suggestFix(Throwable e) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (e instanceof MediaCodec.CodecException) { - MediaCodec.CodecException mce = (MediaCodec.CodecException) e; - if (mce.getErrorCode() == 0xfffffc0e) { - Ln.e("The hardware encoder is not able to encode at the given definition."); - Ln.e("Try with a lower definition:"); - Ln.e(" scrcpy -m 1024"); - } - } - } if (e instanceof InvalidDisplayIdException) { InvalidDisplayIdException idie = (InvalidDisplayIdException) e; int[] displayIds = idie.getAvailableDisplayIds(); From 8fa9e6b01a4b5ee715cc6ba51d95ce8522ccc99c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 15 Jan 2022 23:17:52 +0100 Subject: [PATCH 06/11] Mention auto-downsize feature in FAQ PR #2947 --- FAQ.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/FAQ.md b/FAQ.md index d5f0e3ee5e..43ba39aff4 100644 --- a/FAQ.md +++ b/FAQ.md @@ -219,6 +219,9 @@ scrcpy -m 1024 scrcpy -m 800 ``` +Since scrcpy v1.22, scrcpy automatically tries again with a lower definition +before failing. This behavior can be disabled with `--no-downsize-on-error`. + You could also try another [encoder](README.md#encoder). From 4fb61ac83de7c99edcd1febd9635d822b3d0a075 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 16 Jan 2022 15:27:23 +0100 Subject: [PATCH 07/11] Fix screen comments The position fields accept SC_WINDOW_POSITION_UNDEFINED, not the size fields. PR #2947 --- app/src/screen.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/screen.h b/app/src/screen.h index 4810de31e9..9f65a5f625 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -74,10 +74,10 @@ struct sc_screen_params { struct sc_size frame_size; bool always_on_top; - int16_t window_x; - int16_t window_y; - uint16_t window_width; // accepts SC_WINDOW_POSITION_UNDEFINED - uint16_t window_height; // accepts SC_WINDOW_POSITION_UNDEFINED + int16_t window_x; // accepts SC_WINDOW_POSITION_UNDEFINED + int16_t window_y; // accepts SC_WINDOW_POSITION_UNDEFINED + uint16_t window_width; + uint16_t window_height; bool window_borderless; From fa30f9806aa928da5010c7110e6f0fafb5220eaf Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 16 Jan 2022 15:47:11 +0100 Subject: [PATCH 08/11] Move "show window" call on first frame Show the window only after the actual frame size is known (and if no error has occurred). This will allow to properly position and size the window when the size of the first frame is different from the size initially announced by the server. PR #2947 --- app/src/screen.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/screen.c b/app/src/screen.c index a2796278e4..78ab91d96e 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -693,6 +693,12 @@ sc_screen_update_frame(struct sc_screen *screen) { } update_texture(screen, frame); + if (!screen->has_frame) { + screen->has_frame = true; + // this is the very first frame, show the window + sc_screen_show_window(screen); + } + sc_screen_render(screen, false); return true; } @@ -763,17 +769,13 @@ sc_screen_is_mouse_capture_key(SDL_Keycode key) { bool sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) { switch (event->type) { - case EVENT_NEW_FRAME: - if (!screen->has_frame) { - screen->has_frame = true; - // this is the very first frame, show the window - sc_screen_show_window(screen); - } + case EVENT_NEW_FRAME: { bool ok = sc_screen_update_frame(screen); if (!ok) { LOGW("Frame update failed\n"); } return true; + } case SDL_WINDOWEVENT: if (!screen->has_frame) { // Do nothing From 75c5dc6859d29e2fb4d273e6c261288b802e16c0 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 16 Jan 2022 15:46:37 +0100 Subject: [PATCH 09/11] Position and size the window on first frame The optimal initial size was computed from the expected dimensions, sent immediately by the server before encoding any video frame. However, the actual frame size may be different, for example when the device encoder does not support the requested size. To always handle this case properly, position and size the window only once the first frame size is known. PR #2947 --- app/src/screen.c | 50 ++++++++++++++++++++++++++---------------------- app/src/screen.h | 9 +++++++++ 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/app/src/screen.c b/app/src/screen.c index 78ab91d96e..a357eecf3a 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -369,6 +369,12 @@ sc_screen_init(struct sc_screen *screen, screen->mouse_captured = false; screen->mouse_capture_key_pressed = 0; + screen->req.x = params->window_x; + screen->req.y = params->window_y; + screen->req.width = params->window_width; + screen->req.height = params->window_height; + screen->req.fullscreen = params->fullscreen; + static const struct sc_video_buffer_callbacks cbs = { .on_new_frame = sc_video_buffer_on_new_frame, }; @@ -397,9 +403,6 @@ sc_screen_init(struct sc_screen *screen, get_rotated_size(screen->frame_size, screen->rotation); screen->content_size = content_size; - struct sc_size window_size = - get_initial_optimal_size(content_size,params->window_width, - params->window_height); uint32_t window_flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; @@ -410,13 +413,9 @@ sc_screen_init(struct sc_screen *screen, window_flags |= SDL_WINDOW_BORDERLESS; } - int x = params->window_x != SC_WINDOW_POSITION_UNDEFINED - ? params->window_x : (int) SDL_WINDOWPOS_UNDEFINED; - int y = params->window_y != SC_WINDOW_POSITION_UNDEFINED - ? params->window_y : (int) SDL_WINDOWPOS_UNDEFINED; - screen->window = SDL_CreateWindow(params->window_title, x, y, - window_size.width, window_size.height, - window_flags); + // The window will be positioned and sized on first video frame + screen->window = + SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags); if (!screen->window) { LOGC("Could not create window: %s", SDL_GetError()); goto error_destroy_fps_counter; @@ -498,17 +497,6 @@ sc_screen_init(struct sc_screen *screen, sc_input_manager_init(&screen->im, &im_params); - // Reset the window size to trigger a SIZE_CHANGED event, to workaround - // HiDPI issues with some SDL renderers when several displays having - // different HiDPI scaling are connected - SDL_SetWindowSize(screen->window, window_size.width, window_size.height); - - sc_screen_update_content_rect(screen); - - if (params->fullscreen) { - sc_screen_switch_fullscreen(screen); - } - #ifdef CONTINUOUS_RESIZING_WORKAROUND SDL_AddEventWatch(event_watcher, screen); #endif @@ -545,7 +533,23 @@ sc_screen_init(struct sc_screen *screen, } static void -sc_screen_show_window(struct sc_screen *screen) { +sc_screen_show_initial_window(struct sc_screen *screen) { + int x = screen->req.x != SC_WINDOW_POSITION_UNDEFINED + ? screen->req.x : (int) SDL_WINDOWPOS_CENTERED; + int y = screen->req.y != SC_WINDOW_POSITION_UNDEFINED + ? screen->req.y : (int) SDL_WINDOWPOS_CENTERED; + + struct sc_size window_size = + get_initial_optimal_size(screen->content_size, screen->req.width, + screen->req.height); + + set_window_size(screen, window_size); + SDL_SetWindowPosition(screen->window, x, y); + + if (screen->req.fullscreen) { + sc_screen_switch_fullscreen(screen); + } + SDL_ShowWindow(screen->window); } @@ -696,7 +700,7 @@ sc_screen_update_frame(struct sc_screen *screen) { if (!screen->has_frame) { screen->has_frame = true; // this is the very first frame, show the window - sc_screen_show_window(screen); + sc_screen_show_initial_window(screen); } sc_screen_render(screen, false); diff --git a/app/src/screen.h b/app/src/screen.h index 9f65a5f625..3e6c10313e 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -28,6 +28,15 @@ struct sc_screen { struct sc_video_buffer vb; struct fps_counter fps_counter; + // The initial requested window properties + struct { + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; + bool fullscreen; + } req; + SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *texture; From 3a0ba7d0a439c0933f74e2433122c15cb7ee7318 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 17 Jan 2022 20:16:03 +0100 Subject: [PATCH 10/11] Disable downsizing on error if V4L2 is enabled V4L2 device is created with the initial device size, it does not support resizing. PR #2947 --- app/src/cli.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/cli.c b/app/src/cli.c index 76feb13084..69177aef90 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -1545,11 +1545,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], return false; } - if (opts->v4l2_device && opts->lock_video_orientation - == SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) { - LOGI("Video orientation is locked for v4l2 sink. " - "See --lock-video-orientation."); - opts->lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_INITIAL; + if (opts->v4l2_device) { + if (opts->lock_video_orientation == + SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) { + LOGI("Video orientation is locked for v4l2 sink. " + "See --lock-video-orientation."); + opts->lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_INITIAL; + } + + // 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 (opts->v4l2_buffer && !opts->v4l2_device) { From 2eb6fe7d81c15912a2252f229d317ae03f618a33 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 17 Jan 2022 20:22:33 +0100 Subject: [PATCH 11/11] Downsize on error only before the first frame The purpose of automatic downscaling on error is to make mirroring work by just starting scrcpy without an explicit -m value, even if the encoder could not encode at the screen definition. It is only useful when we detect an encoding failure before the first frame. Downsizing later could be surprising, so disable it. PR #2947 --- .../src/main/java/com/genymobile/scrcpy/ScreenEncoder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index 10ba9fac47..06f06a9de8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -41,6 +41,8 @@ public class ScreenEncoder implements Device.RotationListener { private final boolean downsizeOnError; private long ptsOrigin; + private boolean firstFrameSent; + public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List codecOptions, String encoderName, boolean downsizeOnError) { this.sendFrameMeta = sendFrameMeta; @@ -99,7 +101,7 @@ private void internalStreamScreen(Device device, FileDescriptor fd) throws IOExc codec.stop(); } catch (Exception e) { Ln.e("Encoding error: " + e.getClass().getName() + ": " + e.getMessage()); - if (!downsizeOnError) { + if (!downsizeOnError || firstFrameSent) { // Fail immediately throw e; } @@ -157,6 +159,7 @@ private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException { } IO.writeFully(fd, codecBuffer); + firstFrameSent = true; } } finally { if (outputBufferId >= 0) {