Skip to content

Commit

Permalink
Add --camera-fps
Browse files Browse the repository at this point in the history
Add a new option for specifying the camera frame rate.

By default, Android's default frame rate (30 fps) is used.

PR #4213 <#4213>

Signed-off-by: Andrew Gunnerson <[email protected]>
Signed-off-by: Romain Vimont <[email protected]>
  • Loading branch information
chenxiaolong authored and rom1v committed Oct 31, 2023
1 parent 928f8b8 commit 4722bff
Show file tree
Hide file tree
Showing 13 changed files with 72 additions and 3 deletions.
2 changes: 2 additions & 0 deletions app/data/bash-completion/scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ _scrcpy() {
--camera-ar=
--camera-id=
--camera-facing=
--camera-fps=
--camera-size=
--crop=
-d --select-usb
Expand Down Expand Up @@ -156,6 +157,7 @@ _scrcpy() {
|--audio-output-buffer \
|--camera-ar \
|--camera-id \
|--camera-fps \
|--camera-size \
|--crop \
|--display-id \
Expand Down
1 change: 1 addition & 0 deletions app/data/zsh-completion/_scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ arguments=(
'--camera-ar=[Select the camera size by its aspect ratio]'
'--camera-id=[Specify the camera id to mirror]'
'--camera-facing=[Select the device camera by its facing direction]:facing:(front back external)'
'--camera-fps=[Specify the camera capture frame rate]'
'--camera-size=[Specify an explicit camera capture size]'
'--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]'
{-d,--select-usb}'[Use USB device]'
Expand Down
6 changes: 6 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ Select the device camera by its facing direction.

Possible values are "front", "back" and "external".

.TP
.BI "\-\-camera\-fps " fps
Specify the camera capture frame rate.

If not specified, Android's default frame rate (30 fps) is used.

.TP
.BI "\-\-camera\-size " width\fRx\fIheight
Specify an explicit camera capture size.
Expand Down
27 changes: 27 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ enum {
OPT_CAMERA_SIZE,
OPT_CAMERA_FACING,
OPT_CAMERA_AR,
OPT_CAMERA_FPS,
};

struct sc_option {
Expand Down Expand Up @@ -234,6 +235,14 @@ static const struct sc_option options[] = {
.argdesc = "<width>x<height>",
.text = "Specify an explicit camera capture size.",
},
{
.longopt_id = OPT_CAMERA_FPS,
.longopt = "camera-fps",
.argdesc = "value",
.text = "Specify the camera capture frame rate.\n"
"If not specified, Android's default frame rate (30 fps) is "
"used.",
},
{
// Not really deprecated (--codec has never been released), but without
// declaring an explicit --codec option, getopt_long() partial matching
Expand Down Expand Up @@ -1746,6 +1755,18 @@ parse_camera_facing(const char *optarg, enum sc_camera_facing *facing) {
return false;
}

static bool
parse_camera_fps(const char *s, uint16_t *camera_fps) {
long value;
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "camera fps");
if (!ok) {
return false;
}

*camera_fps = (uint16_t) value;
return true;
}

static bool
parse_time_limit(const char *s, sc_tick *tick) {
long value;
Expand Down Expand Up @@ -2154,6 +2175,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
break;
case OPT_CAMERA_FPS:
if (!parse_camera_fps(optarg, &opts->camera_fps)) {
return false;
}
break;
default:
// getopt prints the error message on stderr
return false;
Expand Down Expand Up @@ -2277,6 +2303,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} else if (opts->camera_id
|| opts->camera_ar
|| opts->camera_facing != SC_CAMERA_FACING_ANY
|| opts->camera_fps
|| opts->camera_size) {
LOGE("Camera options are only available with --video-source=camera");
return false;
Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const struct scrcpy_options scrcpy_options_default = {
.camera_id = NULL,
.camera_size = NULL,
.camera_ar = NULL,
.camera_fps = 0,
.log_level = SC_LOG_LEVEL_INFO,
.video_codec = SC_CODEC_H264,
.audio_codec = SC_CODEC_OPUS,
Expand Down
1 change: 1 addition & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ struct scrcpy_options {
const char *camera_id;
const char *camera_size;
const char *camera_ar;
uint16_t camera_fps;
enum sc_log_level log_level;
enum sc_codec video_codec;
enum sc_codec audio_codec;
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ scrcpy(struct scrcpy_options *options) {
.camera_id = options->camera_id,
.camera_size = options->camera_size,
.camera_ar = options->camera_ar,
.camera_fps = options->camera_fps,
.force_adb_forward = options->force_adb_forward,
.power_off_on_close = options->power_off_on_close,
.clipboard_autosync = options->clipboard_autosync,
Expand Down
3 changes: 3 additions & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ execute_server(struct sc_server *server,
if (params->camera_ar) {
ADD_PARAM("camera_ar=%s", params->camera_ar);
}
if (params->camera_fps) {
ADD_PARAM("camera_fps=%" PRIu16, params->camera_fps);
}
if (params->show_touches) {
ADD_PARAM("show_touches=true");
}
Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct sc_server_params {
const char *camera_id;
const char *camera_size;
const char *camera_ar;
uint16_t camera_fps;
struct sc_port_range port_range;
uint32_t tunnel_host;
uint16_t tunnel_port;
Expand Down
10 changes: 9 additions & 1 deletion server/src/main/java/com/genymobile/scrcpy/CameraCapture.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Range;
import android.view.Surface;

import java.io.IOException;
Expand All @@ -38,6 +39,7 @@ public class CameraCapture extends SurfaceCapture {
private final Size explicitSize;
private int maxSize;
private final CameraAspectRatio aspectRatio;
private final int fps;

private String cameraId;
private Size size;
Expand All @@ -49,12 +51,13 @@ public class CameraCapture extends SurfaceCapture {

private final AtomicBoolean disconnected = new AtomicBoolean();

public CameraCapture(String explicitCameraId, CameraFacing cameraFacing, Size explicitSize, int maxSize, CameraAspectRatio aspectRatio) {
public CameraCapture(String explicitCameraId, CameraFacing cameraFacing, Size explicitSize, int maxSize, CameraAspectRatio aspectRatio, int fps) {
this.explicitCameraId = explicitCameraId;
this.cameraFacing = cameraFacing;
this.explicitSize = explicitSize;
this.maxSize = maxSize;
this.aspectRatio = aspectRatio;
this.fps = fps;
}

@Override
Expand Down Expand Up @@ -304,6 +307,11 @@ public void onConfigureFailed(CameraCaptureSession session) {
private CaptureRequest createCaptureRequest(Surface surface) throws CameraAccessException {
CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
requestBuilder.addTarget(surface);

if (fps > 0) {
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(fps, fps));
}

return requestBuilder.build();
}

Expand Down
12 changes: 11 additions & 1 deletion server/src/main/java/com/genymobile/scrcpy/LogUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.MediaCodec;
import android.util.Range;

import java.util.List;
import java.util.TreeSet;

public final class LogUtils {

Expand Down Expand Up @@ -97,7 +99,15 @@ public static String buildCameraListMessage(boolean includeSizes) {
builder.append(" (").append(getCameraFacingName(facing)).append(", ");

Rect activeSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
builder.append(activeSize.width()).append("x").append(activeSize.height()).append(')');
builder.append(activeSize.width()).append("x").append(activeSize.height()).append(", ");

// Capture frame rates for low-FPS mode are the same for every resolution
Range<Integer>[] lowFpsRanges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
TreeSet<Integer> uniqueLowFps = new TreeSet<>();
for (Range<Integer> range : lowFpsRanges) {
uniqueLowFps.add(range.getUpper());
}
builder.append("fps=").append(uniqueLowFps).append(')');

if (includeSizes) {
StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Expand Down
8 changes: 8 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class Options {
private Size cameraSize;
private CameraFacing cameraFacing;
private CameraAspectRatio cameraAspectRatio;
private int cameraFps;
private boolean showTouches;
private boolean stayAwake;
private List<CodecOption> videoCodecOptions;
Expand Down Expand Up @@ -136,6 +137,10 @@ public CameraAspectRatio getCameraAspectRatio() {
return cameraAspectRatio;
}

public int getCameraFps() {
return cameraFps;
}

public boolean getShowTouches() {
return showTouches;
}
Expand Down Expand Up @@ -384,6 +389,9 @@ public static Options parse(String... args) {
options.cameraAspectRatio = parseCameraAspectRatio(value);
}
break;
case "camera_fps":
options.cameraFps = Integer.parseInt(value);
break;
case "send_device_meta":
options.sendDeviceMeta = Boolean.parseBoolean(value);
break;
Expand Down
2 changes: 1 addition & 1 deletion server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
surfaceCapture = new ScreenCapture(device);
} else {
surfaceCapture = new CameraCapture(options.getCameraId(), options.getCameraFacing(), options.getCameraSize(),
options.getMaxSize(), options.getCameraAspectRatio());
options.getMaxSize(), options.getCameraAspectRatio(), options.getCameraFps());
}
SurfaceEncoder surfaceEncoder = new SurfaceEncoder(surfaceCapture, videoStreamer, options.getVideoBitRate(), options.getMaxFps(),
options.getVideoCodecOptions(), options.getVideoEncoder(), options.getDownsizeOnError());
Expand Down

0 comments on commit 4722bff

Please sign in to comment.