Add option to specify the camera zoom

Add --camera-zoom to specify the camera zoom.

TODO ref 6243.

Signed-off-by: Romain Vimont <rom@rom1v.com>
This commit is contained in:
Tommie
2025-07-20 11:50:16 -04:00
committed by Romain Vimont
parent f00b1a92ba
commit 0f33b97d08
12 changed files with 78 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ _scrcpy() {
--camera-high-speed --camera-high-speed
--camera-size= --camera-size=
--camera-torch --camera-torch
--camera-zoom=
--capture-orientation= --capture-orientation=
--crop= --crop=
-d --select-usb -d --select-usb
@@ -199,6 +200,7 @@ _scrcpy() {
|--camera-fps \ |--camera-fps \
|--camera-size \ |--camera-size \
|--camera-torch \ |--camera-torch \
|--camera-zoom \
|--crop \ |--crop \
|--display-id \ |--display-id \
|--max-fps \ |--max-fps \

View File

@@ -26,6 +26,7 @@ arguments=(
'--camera-fps=[Specify the camera capture frame rate]' '--camera-fps=[Specify the camera capture frame rate]'
'--camera-size=[Specify an explicit camera capture size]' '--camera-size=[Specify an explicit camera capture size]'
'--camera-torch[Turn on the camera torch when the camera starts]' '--camera-torch[Turn on the camera torch when the camera starts]'
'--camera-zoom[Specify the camera zoom initial value]'
'--capture-orientation=[Set the capture video orientation]:orientation:(0 90 180 270 flip0 flip90 flip180 flip270 @0 @90 @180 @270 @flip0 @flip90 @flip180 @flip270)' '--capture-orientation=[Set the capture video orientation]:orientation:(0 90 180 270 flip0 flip90 flip180 flip270 @0 @90 @180 @270 @flip0 @flip90 @flip180 @flip270)'
'--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]' '--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]'
{-d,--select-usb}'[Use USB device]' {-d,--select-usb}'[Use USB device]'

View File

@@ -135,6 +135,10 @@ Specify an explicit camera capture size.
.BI \-\-camera\-torch .BI \-\-camera\-torch
Turn on the camera torch when the camera starts. Turn on the camera torch when the camera starts.
.TP
.BI "\-\-camera-zoom " zoom
Specify the camera zoom initial value.
.TP .TP
.BI "\-\-capture\-orientation " value .BI "\-\-capture\-orientation " value
Possible values are 0, 90, 180, 270, flip0, flip90, flip180 and flip270, possibly prefixed by '@'. Possible values are 0, 90, 180, 270, flip0, flip90, flip180 and flip270, possibly prefixed by '@'.

View File

@@ -115,6 +115,7 @@ enum {
OPT_NO_VD_DESTROY_CONTENT, OPT_NO_VD_DESTROY_CONTENT,
OPT_DISPLAY_IME_POLICY, OPT_DISPLAY_IME_POLICY,
OPT_CAMERA_TORCH, OPT_CAMERA_TORCH,
OPT_CAMERA_ZOOM,
}; };
struct sc_option { struct sc_option {
@@ -319,6 +320,12 @@ static const struct sc_option options[] = {
.longopt = "camera-torch", .longopt = "camera-torch",
.text = "Turn on the camera torch when the camera starts.", .text = "Turn on the camera torch when the camera starts.",
}, },
{
.longopt_id = OPT_CAMERA_ZOOM,
.longopt = "camera-zoom",
.argdesc = "zoom",
.text = "Specify the camera zoom initial value.",
},
{ {
.longopt_id = OPT_CAPTURE_ORIENTATION, .longopt_id = OPT_CAPTURE_ORIENTATION,
.longopt = "capture-orientation", .longopt = "capture-orientation",
@@ -2797,6 +2804,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_CAMERA_TORCH: case OPT_CAMERA_TORCH:
opts->camera_torch = true; opts->camera_torch = true;
break; break;
case OPT_CAMERA_ZOOM:
opts->camera_zoom = optarg;
break;
case OPT_NO_WINDOW: case OPT_NO_WINDOW:
opts->window = false; opts->window = false;
break; break;

View File

@@ -16,6 +16,7 @@ const struct scrcpy_options scrcpy_options_default = {
.camera_id = NULL, .camera_id = NULL,
.camera_size = NULL, .camera_size = NULL,
.camera_ar = NULL, .camera_ar = NULL,
.camera_zoom = NULL,
.camera_fps = 0, .camera_fps = 0,
.log_level = SC_LOG_LEVEL_INFO, .log_level = SC_LOG_LEVEL_INFO,
.video_codec = SC_CODEC_H264, .video_codec = SC_CODEC_H264,

View File

@@ -241,6 +241,7 @@ struct scrcpy_options {
const char *camera_id; const char *camera_id;
const char *camera_size; const char *camera_size;
const char *camera_ar; const char *camera_ar;
const char *camera_zoom;
uint16_t camera_fps; uint16_t camera_fps;
enum sc_log_level log_level; enum sc_log_level log_level;
enum sc_codec video_codec; enum sc_codec video_codec;

View File

@@ -470,6 +470,7 @@ scrcpy(struct scrcpy_options *options) {
.kill_adb_on_close = options->kill_adb_on_close, .kill_adb_on_close = options->kill_adb_on_close,
.camera_high_speed = options->camera_high_speed, .camera_high_speed = options->camera_high_speed,
.camera_torch = options->camera_torch, .camera_torch = options->camera_torch,
.camera_zoom = options->camera_zoom,
.vd_destroy_content = options->vd_destroy_content, .vd_destroy_content = options->vd_destroy_content,
.vd_system_decorations = options->vd_system_decorations, .vd_system_decorations = options->vd_system_decorations,
.list = options->list, .list = options->list,

View File

@@ -360,6 +360,10 @@ execute_server(struct sc_server *server,
if (params->camera_torch) { if (params->camera_torch) {
ADD_PARAM("camera_torch=true"); ADD_PARAM("camera_torch=true");
} }
if (params->camera_zoom) {
VALIDATE_STRING(params->camera_zoom);
ADD_PARAM("camera_zoom=%s", params->camera_zoom);
}
if (params->show_touches) { if (params->show_touches) {
ADD_PARAM("show_touches=true"); ADD_PARAM("show_touches=true");
} }

View File

@@ -35,6 +35,7 @@ struct sc_server_params {
const char *camera_id; const char *camera_id;
const char *camera_size; const char *camera_size;
const char *camera_ar; const char *camera_ar;
const char *camera_zoom;
uint16_t camera_fps; uint16_t camera_fps;
struct sc_port_range port_range; struct sc_port_range port_range;
uint32_t tunnel_host; uint32_t tunnel_host;

View File

@@ -44,6 +44,7 @@ public class Options {
private Size cameraSize; private Size cameraSize;
private CameraFacing cameraFacing; private CameraFacing cameraFacing;
private CameraAspectRatio cameraAspectRatio; private CameraAspectRatio cameraAspectRatio;
private float cameraZoom = 1;
private int cameraFps; private int cameraFps;
private boolean cameraHighSpeed; private boolean cameraHighSpeed;
private boolean cameraTorch; private boolean cameraTorch;
@@ -169,6 +170,10 @@ public class Options {
return cameraAspectRatio; return cameraAspectRatio;
} }
public float getCameraZoom() {
return cameraZoom;
}
public int getCameraFps() { public int getCameraFps() {
return cameraFps; return cameraFps;
} }
@@ -473,6 +478,11 @@ public class Options {
options.cameraAspectRatio = parseCameraAspectRatio(value); options.cameraAspectRatio = parseCameraAspectRatio(value);
} }
break; break;
case "camera_zoom":
if (!value.isEmpty()) {
options.cameraZoom = Float.parseFloat(value);
}
break;
case "camera_fps": case "camera_fps":
options.cameraFps = Integer.parseInt(value); options.cameraFps = Integer.parseInt(value);
break; break;

View File

@@ -23,6 +23,7 @@ import android.media.MediaCodecList;
import android.os.Build; import android.os.Build;
import android.util.Range; import android.util.Range;
import java.text.DecimalFormat;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -172,6 +173,18 @@ public final class LogUtils {
Ln.w("Could not get available frame rates for camera " + id, e); Ln.w("Could not get available frame rates for camera " + id, e);
} }
if (Build.VERSION.SDK_INT >= AndroidVersions.API_30_ANDROID_11) {
try {
Range<Float> zoomRange = characteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE);
if (zoomRange != null) {
String zoom = getFormattedZoomRange(zoomRange);
builder.append(", zoom-range=").append(zoom);
}
} catch (Exception e) {
Ln.w("Could not get available zoom ranges for camera " + id, e);
}
}
builder.append(')'); builder.append(')');
if (includeSizes) { if (includeSizes) {
@@ -226,6 +239,11 @@ public final class LogUtils {
return builder.toString(); return builder.toString();
} }
private static String getFormattedZoomRange(Range<Float> range) {
DecimalFormat format = new DecimalFormat("#.##");
return "[" + format.format(range.getLower()) + ", " + format.format(range.getUpper()) + "]";
}
public static String buildAppListMessage() { public static String buildAppListMessage() {
List<DeviceApp> apps = Device.listApps(); List<DeviceApp> apps = Device.listApps();
return buildAppListMessage("List of apps:", apps); return buildAppListMessage("List of apps:", apps);

View File

@@ -65,10 +65,12 @@ public class CameraCapture extends SurfaceCapture {
private final Orientation captureOrientation; private final Orientation captureOrientation;
private final float angle; private final float angle;
private final boolean initialTorch; private final boolean initialTorch;
private float zoom;
private String cameraId; private String cameraId;
private Size captureSize; private Size captureSize;
private Size videoSize; // after OpenGL transforms private Size videoSize; // after OpenGL transforms
private Range<Float> zoomRange;
private AffineMatrix transform; private AffineMatrix transform;
private OpenGLRunner glRunner; private OpenGLRunner glRunner;
@@ -98,6 +100,7 @@ public class CameraCapture extends SurfaceCapture {
assert captureOrientation != null; assert captureOrientation != null;
this.angle = options.getAngle(); this.angle = options.getAngle();
this.initialTorch = options.getCameraTorch(); this.initialTorch = options.getCameraTorch();
this.zoom = options.getCameraZoom();
} }
@Override @Override
@@ -288,6 +291,14 @@ public class CameraCapture extends SurfaceCapture {
return; return;
} }
CameraManager cameraManager = ServiceManager.getCameraManager();
try {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
zoomRange = characteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE);
} catch (CameraAccessException e) {
Ln.w("Could not get camera characteristics");
}
try { try {
requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
requestBuilder.addTarget(captureSurface); requestBuilder.addTarget(captureSurface);
@@ -299,6 +310,11 @@ public class CameraCapture extends SurfaceCapture {
Ln.i("Turn camera torch on"); Ln.i("Turn camera torch on");
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
} }
if (zoom != 1) {
zoom = clampZoom(zoom);
Ln.i("Set camera zoom: " + zoom);
requestBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoom);
}
CaptureRequest request = requestBuilder.build(); CaptureRequest request = requestBuilder.build();
setRepeatingRequest(session, request); setRepeatingRequest(session, request);
@@ -456,6 +472,15 @@ public class CameraCapture extends SurfaceCapture {
}); });
} }
private float clampZoom(float value) {
assertCameraThread();
if (zoomRange == null) {
return value;
}
return zoomRange.clamp(value);
}
private void assertCameraThread() { private void assertCameraThread() {
assert Thread.currentThread() == cameraThread; assert Thread.currentThread() == cameraThread;
} }