mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-12-18 14:04:20 +01:00
Add shortcuts to change the camera zoom
MOD+up and MOD+zoom change the camera zoom. TODO ref 6243 Signed-off-by: Romain Vimont <rom@rom1v.com>
This commit is contained in:
@@ -191,6 +191,8 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
|
|||||||
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||||
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
||||||
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
|
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN:
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT:
|
||||||
// no additional data
|
// no additional data
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
@@ -325,6 +327,12 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
|
|||||||
LOG_CMSG("camera set torch %s",
|
LOG_CMSG("camera set torch %s",
|
||||||
msg->camera_set_torch.on ? "on" : "off");
|
msg->camera_set_torch.on ? "on" : "off");
|
||||||
break;
|
break;
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN:
|
||||||
|
LOG_CMSG("camera zoom in");
|
||||||
|
break;
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT:
|
||||||
|
LOG_CMSG("camera zoom out");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_CMSG("unknown type: %u", (unsigned) msg->type);
|
LOG_CMSG("unknown type: %u", (unsigned) msg->type);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ enum sc_control_msg_type {
|
|||||||
SC_CONTROL_MSG_TYPE_START_APP,
|
SC_CONTROL_MSG_TYPE_START_APP,
|
||||||
SC_CONTROL_MSG_TYPE_RESET_VIDEO,
|
SC_CONTROL_MSG_TYPE_RESET_VIDEO,
|
||||||
SC_CONTROL_MSG_TYPE_CAMERA_SET_TORCH,
|
SC_CONTROL_MSG_TYPE_CAMERA_SET_TORCH,
|
||||||
|
SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN,
|
||||||
|
SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sc_copy_key {
|
enum sc_copy_key {
|
||||||
|
|||||||
@@ -315,6 +315,30 @@ camera_set_torch(struct sc_input_manager *im, bool on) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camera_zoom_in(struct sc_input_manager *im) {
|
||||||
|
assert(im->controller && im->camera);
|
||||||
|
|
||||||
|
struct sc_control_msg msg;
|
||||||
|
msg.type = SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN;
|
||||||
|
|
||||||
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||||
|
LOGW("Could not request camera zoom in");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camera_zoom_out(struct sc_input_manager *im) {
|
||||||
|
assert(im->controller && im->camera);
|
||||||
|
|
||||||
|
struct sc_control_msg msg;
|
||||||
|
msg.type = SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT;
|
||||||
|
|
||||||
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||||
|
LOGW("Could not request camera zoom out");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
apply_orientation_transform(struct sc_input_manager *im,
|
apply_orientation_transform(struct sc_input_manager *im,
|
||||||
enum sc_orientation transform) {
|
enum sc_orientation transform) {
|
||||||
@@ -601,6 +625,18 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
camera_set_torch(im, !shift);
|
camera_set_torch(im, !shift);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
case SDLK_DOWN:
|
||||||
|
if (!shift && down && !paused) {
|
||||||
|
// forward repeated events
|
||||||
|
camera_zoom_out(im);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_UP:
|
||||||
|
if (!shift && down && !paused) {
|
||||||
|
// forward repeated events
|
||||||
|
camera_zoom_in(im);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -465,6 +465,36 @@ static void test_serialize_camera_set_torch(void) {
|
|||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_serialize_camera_zoom_in(void) {
|
||||||
|
struct sc_control_msg msg = {
|
||||||
|
.type = SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN,
|
||||||
|
};
|
||||||
|
|
||||||
|
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_CAMERA_ZOOM_IN,
|
||||||
|
};
|
||||||
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_serialize_camera_zoom_out(void) {
|
||||||
|
struct sc_control_msg msg = {
|
||||||
|
.type = SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
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_CAMERA_ZOOM_OUT,
|
||||||
|
};
|
||||||
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
@@ -490,5 +520,7 @@ int main(int argc, char *argv[]) {
|
|||||||
test_serialize_start_app();
|
test_serialize_start_app();
|
||||||
test_serialize_reset_video();
|
test_serialize_reset_video();
|
||||||
test_serialize_camera_set_torch();
|
test_serialize_camera_set_torch();
|
||||||
|
test_serialize_camera_zoom_in();
|
||||||
|
test_serialize_camera_zoom_out();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ public final class ControlMessage {
|
|||||||
public static final int TYPE_START_APP = 16;
|
public static final int TYPE_START_APP = 16;
|
||||||
public static final int TYPE_RESET_VIDEO = 17;
|
public static final int TYPE_RESET_VIDEO = 17;
|
||||||
public static final int TYPE_CAMERA_SET_TORCH = 18;
|
public static final int TYPE_CAMERA_SET_TORCH = 18;
|
||||||
|
public static final int TYPE_CAMERA_ZOOM_IN = 19;
|
||||||
|
public static final int TYPE_CAMERA_ZOOM_OUT = 20;
|
||||||
|
|
||||||
public static final long SEQUENCE_INVALID = 0;
|
public static final long SEQUENCE_INVALID = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ public class ControlMessageReader {
|
|||||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
||||||
case ControlMessage.TYPE_RESET_VIDEO:
|
case ControlMessage.TYPE_RESET_VIDEO:
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_IN:
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_OUT:
|
||||||
return ControlMessage.createEmpty(type);
|
return ControlMessage.createEmpty(type);
|
||||||
case ControlMessage.TYPE_UHID_CREATE:
|
case ControlMessage.TYPE_UHID_CREATE:
|
||||||
return parseUhidCreate();
|
return parseUhidCreate();
|
||||||
|
|||||||
@@ -376,6 +376,12 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
case ControlMessage.TYPE_CAMERA_SET_TORCH:
|
case ControlMessage.TYPE_CAMERA_SET_TORCH:
|
||||||
cameraCapture.setTorchEnabled(msg.getOn());
|
cameraCapture.setTorchEnabled(msg.getOn());
|
||||||
return true;
|
return true;
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_IN:
|
||||||
|
cameraCapture.zoomIn();
|
||||||
|
return true;
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_OUT:
|
||||||
|
cameraCapture.zoomOut();
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
0, 1, 0, 1, // column 4
|
0, 1, 0, 1, // column 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final float ZOOM_FACTOR = 1 + 1 / 16f;
|
||||||
|
|
||||||
private final String explicitCameraId;
|
private final String explicitCameraId;
|
||||||
private final CameraFacing cameraFacing;
|
private final CameraFacing cameraFacing;
|
||||||
private final Size explicitSize;
|
private final Size explicitSize;
|
||||||
@@ -472,6 +474,36 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void zoom(boolean in) {
|
||||||
|
cameraHandler.post(() -> {
|
||||||
|
assertCameraThread();
|
||||||
|
if (currentSession != null && requestBuilder != null) {
|
||||||
|
// Always align to log values
|
||||||
|
double z = Math.round(Math.log(zoom) / Math.log(ZOOM_FACTOR));
|
||||||
|
double dir = in ? 1 : -1;
|
||||||
|
zoom = (float) Math.pow(ZOOM_FACTOR, z + dir);
|
||||||
|
|
||||||
|
try {
|
||||||
|
zoom = clampZoom(zoom);
|
||||||
|
Ln.i("Set camera zoom: " + zoom);
|
||||||
|
requestBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoom);
|
||||||
|
CaptureRequest request = requestBuilder.build();
|
||||||
|
setRepeatingRequest(currentSession, request);
|
||||||
|
} catch (CameraAccessException e) {
|
||||||
|
Ln.e("Camera error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void zoomIn() {
|
||||||
|
zoom(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void zoomOut() {
|
||||||
|
zoom(false);
|
||||||
|
}
|
||||||
|
|
||||||
private float clampZoom(float value) {
|
private float clampZoom(float value) {
|
||||||
assertCameraThread();
|
assertCameraThread();
|
||||||
if (zoomRange == null) {
|
if (zoomRange == null) {
|
||||||
|
|||||||
@@ -440,6 +440,38 @@ public class ControlMessageReaderTest {
|
|||||||
Assert.assertEquals(-1, bis.read()); // EOS
|
Assert.assertEquals(-1, bis.read()); // EOS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseCameraZoomIn() throws IOException {
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
|
dos.writeByte(ControlMessage.TYPE_CAMERA_ZOOM_IN);
|
||||||
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||||
|
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||||
|
|
||||||
|
ControlMessage event = reader.read();
|
||||||
|
Assert.assertEquals(ControlMessage.TYPE_CAMERA_ZOOM_IN, event.getType());
|
||||||
|
|
||||||
|
Assert.assertEquals(-1, bis.read()); // EOS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseCameraZoomIn() throws IOException {
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
|
dos.writeByte(ControlMessage.TYPE_CAMERA_ZOOM_OUT);
|
||||||
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||||
|
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||||
|
|
||||||
|
ControlMessage event = reader.read();
|
||||||
|
Assert.assertEquals(ControlMessage.TYPE_CAMERA_ZOOM_OUT, event.getType());
|
||||||
|
|
||||||
|
Assert.assertEquals(-1, bis.read()); // EOS
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiEvents() throws IOException {
|
public void testMultiEvents() throws IOException {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
|||||||
Reference in New Issue
Block a user