mirror of
https://github.com/Genymobile/scrcpy
synced 2025-01-20 15:00:46 +00:00
Accept float values for --max-fps
Android accepts a float value, there is no reason to limit the option to be an integer. In particular, it allows to capture at a rate lower than 1 fps. For example, to capture 1 frame every 5 seconds: scrcpy --video-source=camera --max-fps=0.2 It was already possible to pass a float manually: scrcpy --video-source=camera \ --video-codec-options=max-fps-to-encoder:float=0.2 But accepting a float directly for --max-fps is more convenient. Refs <https://developer.android.com/reference/android/media/MediaFormat#KEY_MAX_FPS_TO_ENCODER>
This commit is contained in:
parent
6451ad271a
commit
265a15e0b1
@ -1447,6 +1447,26 @@ parse_integers_arg(const char *s, const char sep, size_t max_items, long *out,
|
||||
return count;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_float_arg(const char *s, float *out, float min, float max,
|
||||
const char *name) {
|
||||
float value;
|
||||
bool ok = sc_str_parse_float(s, &value);
|
||||
if (!ok) {
|
||||
LOGE("Could not parse %s: %s", name, s);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value < min || value > max) {
|
||||
LOGE("Could not parse %s: value (%f) out-of-range (%f; %f)",
|
||||
name, value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_bit_rate(const char *s, uint32_t *bit_rate) {
|
||||
long value;
|
||||
@ -1474,14 +1494,14 @@ parse_max_size(const char *s, uint16_t *max_size) {
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_max_fps(const char *s, uint16_t *max_fps) {
|
||||
long value;
|
||||
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "max fps");
|
||||
parse_max_fps(const char *s, float *max_fps) {
|
||||
float value;
|
||||
bool ok = parse_float_arg(s, &value, 0, (float) (1 << 16), "max fps");
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*max_fps = (uint16_t) value;
|
||||
*max_fps = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ struct scrcpy_options {
|
||||
uint16_t max_size;
|
||||
uint32_t video_bit_rate;
|
||||
uint32_t audio_bit_rate;
|
||||
uint16_t max_fps;
|
||||
float max_fps;
|
||||
enum sc_lock_video_orientation lock_video_orientation;
|
||||
enum sc_orientation display_orientation;
|
||||
enum sc_orientation record_orientation;
|
||||
|
@ -321,7 +321,7 @@ execute_server(struct sc_server *server,
|
||||
ADD_PARAM("max_size=%" PRIu16, params->max_size);
|
||||
}
|
||||
if (params->max_fps) {
|
||||
ADD_PARAM("max_fps=%" PRIu16, params->max_fps);
|
||||
ADD_PARAM("max_fps=%f" , params->max_fps);
|
||||
}
|
||||
if (params->lock_video_orientation != SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
||||
ADD_PARAM("lock_video_orientation=%" PRIi8,
|
||||
|
@ -44,7 +44,7 @@ struct sc_server_params {
|
||||
uint16_t max_size;
|
||||
uint32_t video_bit_rate;
|
||||
uint32_t audio_bit_rate;
|
||||
uint16_t max_fps;
|
||||
float max_fps;
|
||||
int8_t lock_video_orientation;
|
||||
bool control;
|
||||
uint32_t display_id;
|
||||
|
@ -147,6 +147,25 @@ sc_str_parse_integer_with_suffix(const char *s, long *out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_str_parse_float(const char *s, float *out) {
|
||||
char *endptr;
|
||||
if (*s == '\0') {
|
||||
return false;
|
||||
}
|
||||
errno = 0;
|
||||
float value = strtof(s, &endptr);
|
||||
if (errno == ERANGE) {
|
||||
return false;
|
||||
}
|
||||
if (*endptr != '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_str_list_contains(const char *list, char sep, const char *s) {
|
||||
char *p;
|
||||
|
@ -66,6 +66,14 @@ sc_str_parse_integers(const char *s, const char sep, size_t max_items,
|
||||
bool
|
||||
sc_str_parse_integer_with_suffix(const char *s, long *out);
|
||||
|
||||
/**
|
||||
* `Parse `s` as a float into `out`
|
||||
*
|
||||
* Return true if the conversion succeeded, false otherwise.
|
||||
*/
|
||||
bool
|
||||
sc_str_parse_float(const char *s, float *out);
|
||||
|
||||
/**
|
||||
* Search `s` in the list separated by `sep`
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ public class Options {
|
||||
private boolean audioDup;
|
||||
private int videoBitRate = 8000000;
|
||||
private int audioBitRate = 128000;
|
||||
private int maxFps;
|
||||
private float maxFps;
|
||||
private int lockVideoOrientation = -1;
|
||||
private boolean tunnelForward;
|
||||
private Rect crop;
|
||||
@ -113,7 +113,7 @@ public class Options {
|
||||
return audioBitRate;
|
||||
}
|
||||
|
||||
public int getMaxFps() {
|
||||
public float getMaxFps() {
|
||||
return maxFps;
|
||||
}
|
||||
|
||||
@ -321,7 +321,7 @@ public class Options {
|
||||
options.audioBitRate = Integer.parseInt(value);
|
||||
break;
|
||||
case "max_fps":
|
||||
options.maxFps = Integer.parseInt(value);
|
||||
options.maxFps = parseFloat("max_fps", value);
|
||||
break;
|
||||
case "lock_video_orientation":
|
||||
options.lockVideoOrientation = Integer.parseInt(value);
|
||||
@ -493,4 +493,12 @@ public class Options {
|
||||
float floatAr = Float.parseFloat(tokens[0]);
|
||||
return CameraAspectRatio.fromFloat(floatAr);
|
||||
}
|
||||
|
||||
private static float parseFloat(String key, String value) {
|
||||
try {
|
||||
return Float.parseFloat(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid float value for " + key + ": \"" + value + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
private final String encoderName;
|
||||
private final List<CodecOption> codecOptions;
|
||||
private final int videoBitRate;
|
||||
private final int maxFps;
|
||||
private final float maxFps;
|
||||
private final boolean downsizeOnError;
|
||||
|
||||
private boolean firstFrameSent;
|
||||
@ -48,8 +48,8 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
private Thread thread;
|
||||
private final AtomicBoolean stopped = new AtomicBoolean();
|
||||
|
||||
public SurfaceEncoder(SurfaceCapture capture, Streamer streamer, int videoBitRate, int maxFps, List<CodecOption> codecOptions, String encoderName,
|
||||
boolean downsizeOnError) {
|
||||
public SurfaceEncoder(SurfaceCapture capture, Streamer streamer, int videoBitRate, float maxFps, List<CodecOption> codecOptions,
|
||||
String encoderName, boolean downsizeOnError) {
|
||||
this.capture = capture;
|
||||
this.streamer = streamer;
|
||||
this.videoBitRate = videoBitRate;
|
||||
@ -225,7 +225,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static MediaFormat createFormat(String videoMimeType, int bitRate, int maxFps, List<CodecOption> codecOptions) {
|
||||
private static MediaFormat createFormat(String videoMimeType, int bitRate, float maxFps, List<CodecOption> codecOptions) {
|
||||
MediaFormat format = new MediaFormat();
|
||||
format.setString(MediaFormat.KEY_MIME, videoMimeType);
|
||||
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
|
||||
|
Loading…
Reference in New Issue
Block a user