Skip to content

Commit

Permalink
Add shortcut to reset video capture/encoding
Browse files Browse the repository at this point in the history
Reset video capture/encoding on MOD+Shift+r.

Like on device rotation, this starts a new encoding session which
produces a video stream starting by a key frame.

PR #5432 <#5432>
  • Loading branch information
rom1v committed Nov 3, 2024
1 parent 9958302 commit 3a39d1b
Show file tree
Hide file tree
Showing 15 changed files with 106 additions and 8 deletions.
4 changes: 4 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
Expand Down
4 changes: 4 additions & 0 deletions app/src/control_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions app/src/control_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 18 additions & 2 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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:
Expand Down
16 changes: 16 additions & 0 deletions app/tests/test_control_msg_serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
1 change: 1 addition & 0 deletions doc/shortcuts.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
| Flip display vertically | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>↑</kbd> _(up)_ \| <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>↓</kbd> _(down)_
| Pause or re-pause display | <kbd>MOD</kbd>+<kbd>z</kbd>
| Unpause display | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>z</kbd>
| Reset video capture/encoding | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbr>r</kbd>
| Resize window to 1:1 (pixel-perfect) | <kbd>MOD</kbd>+<kbd>g</kbd>
| Resize window to remove black borders | <kbd>MOD</kbd>+<kbd>w</kbd> \| _Double-left-click¹_
| Click on `HOME` | <kbd>MOD</kbd>+<kbd>h</kbd> \| _Middle-click_
Expand Down
4 changes: 4 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.setSurfaceCapture(surfaceCapture);
}
}

Completion completion = new Completion(asyncProcessors.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
18 changes: 18 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/control/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.SurfaceCapture;
import com.genymobile.scrcpy.video.VirtualDisplayListener;
import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.InputManager;
Expand Down Expand Up @@ -93,6 +94,9 @@ private DisplayData(int virtualDisplayId, PositionMapper positionMapper) {

private boolean keepDisplayPowerOff;

// Used for resetting video encoding on RESET_VIDEO message
private SurfaceCapture surfaceCapture;

public Controller(int displayId, ControlChannel controlChannel, CleanUp cleanUp, boolean clipboardAutosync, boolean powerOn) {
this.displayId = displayId;
this.controlChannel = controlChannel;
Expand Down Expand Up @@ -143,6 +147,10 @@ public void onNewVirtualDisplay(int virtualDisplayId, PositionMapper positionMap
}
}

public void setSurfaceCapture(SurfaceCapture surfaceCapture) {
this.surfaceCapture = surfaceCapture;
}

private UhidManager getUhidManager() {
if (uhidManager == null) {
uhidManager = new UhidManager(sender);
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -680,4 +691,11 @@ private void setDisplayPower(boolean on) {
}
}
}

private void resetVideo() {
if (surfaceCapture != null) {
Ln.i("Video capture reset");
surfaceCapture.requestInvalidate();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,9 @@ public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request
public boolean isClosed() {
return disconnected.get();
}

@Override
public void requestInvalidate() {
// do nothing (the user could not request a reset anyway for now, since there is no controller for camera mirroring)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import android.os.Build;
import android.view.Surface;

import java.io.IOException;

public class NewDisplayCapture extends SurfaceCapture {

// Internal fields copied from android.hardware.display.DisplayManager
Expand Down Expand Up @@ -72,13 +74,8 @@ public void prepare() {
}
}

@Override
public void start(Surface surface) {
if (virtualDisplay != null) {
virtualDisplay.release();
virtualDisplay = null;
}

public void startNew(Surface surface) {
int virtualDisplayId;
try {
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
Expand Down Expand Up @@ -113,6 +110,15 @@ public void start(Surface surface) {
}
}

@Override
public void start(Surface surface) throws IOException {
if (virtualDisplay == null) {
startNew(surface);
} else {
virtualDisplay.setSurface(surface);
}
}

@Override
public void release() {
if (virtualDisplay != null) {
Expand Down Expand Up @@ -142,4 +148,9 @@ private static int scaleDpi(Size initialSize, int initialDpi, Size size) {
int num = size.getMax();
return initialDpi * num / den;
}

@Override
public void requestInvalidate() {
invalidate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,9 @@ private void unregisterDisplayListenerFallbacks() {
}
}
}

@Override
public void requestInvalidate() {
invalidate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,11 @@ public void prepare() throws ConfigurationException {
public boolean isClosed() {
return false;
}

/**
* Manually request to invalidate (typically a user request).
* <p>
* The capture implementation is free to ignore the request and do nothing.
*/
public abstract void requestInvalidate();
}

0 comments on commit 3a39d1b

Please sign in to comment.