diff --git a/app/scrcpy.1 b/app/scrcpy.1 index f517ad4c93..76e36dcb12 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -671,6 +671,10 @@ Pause or re-pause display .B MOD+Shift+z Unpause display +.TP +.B MOD+Shift+r +Reset video capture/encoding + .TP .B MOD+g Resize window to 1:1 (pixel\-perfect) diff --git a/app/src/cli.c b/app/src/cli.c index 77747e93f1..7cc680859b 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -1022,6 +1022,10 @@ static const struct sc_shortcut shortcuts[] = { .shortcuts = { "MOD+Shift+z" }, .text = "Unpause display", }, + { + .shortcuts = { "MOD+Shift+r" }, + .text = "Reset video capture/encoding", + }, { .shortcuts = { "MOD+g" }, .text = "Resize window to 1:1 (pixel-perfect)", diff --git a/app/src/control_msg.c b/app/src/control_msg.c index e04fbd3cf6..0defda9257 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -181,6 +181,7 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) { case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS: case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE: case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS: + case SC_CONTROL_MSG_TYPE_RESET_VIDEO: // no additional data return 1; default: @@ -304,6 +305,9 @@ sc_control_msg_log(const struct sc_control_msg *msg) { case SC_CONTROL_MSG_TYPE_START_APP: LOG_CMSG("start app \"%s\"", msg->start_app.name); break; + case SC_CONTROL_MSG_TYPE_RESET_VIDEO: + LOG_CMSG("reset video"); + break; default: LOG_CMSG("unknown type: %u", (unsigned) msg->type); break; diff --git a/app/src/control_msg.h b/app/src/control_msg.h index 9eef7e827c..f0a2e37346 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -42,6 +42,7 @@ enum sc_control_msg_type { SC_CONTROL_MSG_TYPE_UHID_DESTROY, SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS, SC_CONTROL_MSG_TYPE_START_APP, + SC_CONTROL_MSG_TYPE_RESET_VIDEO, }; enum sc_copy_key { diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 140b50ac6e..3955c211d7 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -284,6 +284,18 @@ open_hard_keyboard_settings(struct sc_input_manager *im) { } } +static void +reset_video(struct sc_input_manager *im) { + assert(im->controller); + + struct sc_control_msg msg; + msg.type = SC_CONTROL_MSG_TYPE_RESET_VIDEO; + + if (!sc_controller_push_msg(im->controller, &msg)) { + LOGW("Could not request reset video"); + } +} + static void apply_orientation_transform(struct sc_input_manager *im, enum sc_orientation transform) { @@ -521,8 +533,12 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_r: - if (control && !shift && !repeat && down && !paused) { - rotate_device(im); + if (control && !repeat && down && !paused) { + if (shift) { + reset_video(im); + } else { + rotate_device(im); + } } return; case SDLK_k: diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index 73bca901f9..9adf2a3d7f 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -407,6 +407,21 @@ static void test_serialize_open_hard_keyboard(void) { assert(!memcmp(buf, expected, sizeof(expected))); } +static void test_serialize_reset_video(void) { + struct sc_control_msg msg = { + .type = SC_CONTROL_MSG_TYPE_RESET_VIDEO, + }; + + uint8_t buf[SC_CONTROL_MSG_MAX_SIZE]; + size_t size = sc_control_msg_serialize(&msg, buf); + assert(size == 1); + + const uint8_t expected[] = { + SC_CONTROL_MSG_TYPE_RESET_VIDEO, + }; + assert(!memcmp(buf, expected, sizeof(expected))); +} + int main(int argc, char *argv[]) { (void) argc; (void) argv; @@ -429,5 +444,6 @@ int main(int argc, char *argv[]) { test_serialize_uhid_input(); test_serialize_uhid_destroy(); test_serialize_open_hard_keyboard(); + test_serialize_reset_video(); return 0; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index a0a48806c5..e4faaa1b21 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -225,6 +225,10 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc SurfaceEncoder surfaceEncoder = new SurfaceEncoder(surfaceCapture, videoStreamer, options.getVideoBitRate(), options.getMaxFps(), options.getVideoCodecOptions(), options.getVideoEncoder(), options.getDownsizeOnError()); asyncProcessors.add(surfaceEncoder); + + if (controller != null) { + controller.setSurfaceEncoder(surfaceEncoder); + } } Completion completion = new Completion(asyncProcessors.size()); diff --git a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java index eec5f67fb1..7455cdf836 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java @@ -24,6 +24,7 @@ public final class ControlMessage { public static final int TYPE_UHID_DESTROY = 14; public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15; public static final int TYPE_START_APP = 16; + public static final int TYPE_RESET_VIDEO = 17; public static final long SEQUENCE_INVALID = 0; diff --git a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java index ae1676906e..b82615ede8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java @@ -46,6 +46,7 @@ public ControlMessage read() throws IOException { case ControlMessage.TYPE_COLLAPSE_PANELS: case ControlMessage.TYPE_ROTATE_DEVICE: case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS: + case ControlMessage.TYPE_RESET_VIDEO: return ControlMessage.createEmpty(type); case ControlMessage.TYPE_UHID_CREATE: return parseUhidCreate(); diff --git a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java index 76d62fa682..05aee9b709 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java @@ -9,6 +9,7 @@ import com.genymobile.scrcpy.device.Position; import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.LogUtils; +import com.genymobile.scrcpy.video.SurfaceEncoder; import com.genymobile.scrcpy.video.VirtualDisplayListener; import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.InputManager; @@ -93,6 +94,9 @@ private DisplayData(int virtualDisplayId, PositionMapper positionMapper) { private boolean keepDisplayPowerOff; + // Used for resetting video capture/encoding on RESET_VIDEO message + private SurfaceEncoder surfaceEncoder; + public Controller(int displayId, ControlChannel controlChannel, CleanUp cleanUp, boolean clipboardAutosync, boolean powerOn) { this.displayId = displayId; this.controlChannel = controlChannel; @@ -143,6 +147,10 @@ public void onNewVirtualDisplay(int virtualDisplayId, PositionMapper positionMap } } + public void setSurfaceEncoder(SurfaceEncoder surfaceEncoder) { + this.surfaceEncoder = surfaceEncoder; + } + private UhidManager getUhidManager() { if (uhidManager == null) { uhidManager = new UhidManager(sender); @@ -293,6 +301,9 @@ private boolean handleEvent() throws IOException { case ControlMessage.TYPE_START_APP: startAppAsync(msg.getText()); break; + case ControlMessage.TYPE_RESET_VIDEO: + resetVideo(); + break; default: // do nothing } @@ -680,4 +691,11 @@ private void setDisplayPower(boolean on) { } } } + + private void resetVideo() { + if (surfaceEncoder != null) { + Ln.i("Video capture reset"); + surfaceEncoder.requestReset(); + } + } } 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 440d7dcadd..915dca3750 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java @@ -309,4 +309,11 @@ public void join() throws InterruptedException { thread.join(); } } + + /** + * Reset the current encoding (as a consequence of an explicit request from the user). + */ + public void requestReset() { + reset.reset(); + } }