mirror of https://github.com/Genymobile/scrcpy
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
355 lines
12 KiB
Java
355 lines
12 KiB
Java
package com.genymobile.scrcpy;
|
|
|
|
import android.graphics.Rect;
|
|
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
public class Options {
|
|
|
|
private Ln.Level logLevel = Ln.Level.DEBUG;
|
|
private int scid = -1; // 31-bit non-negative value, or -1
|
|
private boolean video = true;
|
|
private boolean audio = true;
|
|
private int maxSize;
|
|
private VideoCodec videoCodec = VideoCodec.H264;
|
|
private AudioCodec audioCodec = AudioCodec.OPUS;
|
|
private AudioSource audioSource = AudioSource.OUTPUT;
|
|
private int videoBitRate = 8000000;
|
|
private int audioBitRate = 128000;
|
|
private int maxFps;
|
|
private int lockVideoOrientation = -1;
|
|
private boolean tunnelForward;
|
|
private Rect crop;
|
|
private boolean control = true;
|
|
private int displayId;
|
|
private boolean showTouches;
|
|
private boolean stayAwake;
|
|
private List<CodecOption> videoCodecOptions;
|
|
private List<CodecOption> audioCodecOptions;
|
|
|
|
private String videoEncoder;
|
|
private String audioEncoder;
|
|
private boolean powerOffScreenOnClose;
|
|
private boolean clipboardAutosync = true;
|
|
private boolean downsizeOnError = true;
|
|
private boolean cleanup = true;
|
|
private boolean powerOn = true;
|
|
|
|
private boolean listEncoders;
|
|
private boolean listDisplays;
|
|
|
|
// Options not used by the scrcpy client, but useful to use scrcpy-server directly
|
|
private boolean sendDeviceMeta = true; // send device name and size
|
|
private boolean sendFrameMeta = true; // send PTS so that the client may record properly
|
|
private boolean sendDummyByte = true; // write a byte on start to detect connection issues
|
|
private boolean sendCodecMeta = true; // write the codec metadata before the stream
|
|
|
|
public Ln.Level getLogLevel() {
|
|
return logLevel;
|
|
}
|
|
|
|
public int getScid() {
|
|
return scid;
|
|
}
|
|
|
|
public boolean getVideo() {
|
|
return video;
|
|
}
|
|
|
|
public boolean getAudio() {
|
|
return audio;
|
|
}
|
|
|
|
public int getMaxSize() {
|
|
return maxSize;
|
|
}
|
|
|
|
public VideoCodec getVideoCodec() {
|
|
return videoCodec;
|
|
}
|
|
|
|
public AudioCodec getAudioCodec() {
|
|
return audioCodec;
|
|
}
|
|
|
|
public AudioSource getAudioSource() {
|
|
return audioSource;
|
|
}
|
|
|
|
public int getVideoBitRate() {
|
|
return videoBitRate;
|
|
}
|
|
|
|
public int getAudioBitRate() {
|
|
return audioBitRate;
|
|
}
|
|
|
|
public int getMaxFps() {
|
|
return maxFps;
|
|
}
|
|
|
|
public int getLockVideoOrientation() {
|
|
return lockVideoOrientation;
|
|
}
|
|
|
|
public boolean isTunnelForward() {
|
|
return tunnelForward;
|
|
}
|
|
|
|
public Rect getCrop() {
|
|
return crop;
|
|
}
|
|
|
|
public boolean getControl() {
|
|
return control;
|
|
}
|
|
|
|
public int getDisplayId() {
|
|
return displayId;
|
|
}
|
|
|
|
public boolean getShowTouches() {
|
|
return showTouches;
|
|
}
|
|
|
|
public boolean getStayAwake() {
|
|
return stayAwake;
|
|
}
|
|
|
|
public List<CodecOption> getVideoCodecOptions() {
|
|
return videoCodecOptions;
|
|
}
|
|
|
|
public List<CodecOption> getAudioCodecOptions() {
|
|
return audioCodecOptions;
|
|
}
|
|
|
|
public String getVideoEncoder() {
|
|
return videoEncoder;
|
|
}
|
|
|
|
public String getAudioEncoder() {
|
|
return audioEncoder;
|
|
}
|
|
|
|
public boolean getPowerOffScreenOnClose() {
|
|
return this.powerOffScreenOnClose;
|
|
}
|
|
|
|
public boolean getClipboardAutosync() {
|
|
return clipboardAutosync;
|
|
}
|
|
|
|
public boolean getDownsizeOnError() {
|
|
return downsizeOnError;
|
|
}
|
|
|
|
public boolean getCleanup() {
|
|
return cleanup;
|
|
}
|
|
|
|
public boolean getPowerOn() {
|
|
return powerOn;
|
|
}
|
|
|
|
public boolean getListEncoders() {
|
|
return listEncoders;
|
|
}
|
|
|
|
public boolean getListDisplays() {
|
|
return listDisplays;
|
|
}
|
|
|
|
public boolean getSendDeviceMeta() {
|
|
return sendDeviceMeta;
|
|
}
|
|
|
|
public boolean getSendFrameMeta() {
|
|
return sendFrameMeta;
|
|
}
|
|
|
|
public boolean getSendDummyByte() {
|
|
return sendDummyByte;
|
|
}
|
|
|
|
public boolean getSendCodecMeta() {
|
|
return sendCodecMeta;
|
|
}
|
|
|
|
@SuppressWarnings("MethodLength")
|
|
public static Options parse(String... args) {
|
|
if (args.length < 1) {
|
|
throw new IllegalArgumentException("Missing client version");
|
|
}
|
|
|
|
String clientVersion = args[0];
|
|
if (!clientVersion.equals(BuildConfig.VERSION_NAME)) {
|
|
throw new IllegalArgumentException(
|
|
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
|
|
}
|
|
|
|
Options options = new Options();
|
|
|
|
for (int i = 1; i < args.length; ++i) {
|
|
String arg = args[i];
|
|
int equalIndex = arg.indexOf('=');
|
|
if (equalIndex == -1) {
|
|
throw new IllegalArgumentException("Invalid key=value pair: \"" + arg + "\"");
|
|
}
|
|
String key = arg.substring(0, equalIndex);
|
|
String value = arg.substring(equalIndex + 1);
|
|
switch (key) {
|
|
case "scid":
|
|
int scid = Integer.parseInt(value, 0x10);
|
|
if (scid < -1) {
|
|
throw new IllegalArgumentException("scid may not be negative (except -1 for 'none'): " + scid);
|
|
}
|
|
options.scid = scid;
|
|
break;
|
|
case "log_level":
|
|
options.logLevel = Ln.Level.valueOf(value.toUpperCase(Locale.ENGLISH));
|
|
break;
|
|
case "video":
|
|
options.video = Boolean.parseBoolean(value);
|
|
break;
|
|
case "audio":
|
|
options.audio = Boolean.parseBoolean(value);
|
|
break;
|
|
case "video_codec":
|
|
VideoCodec videoCodec = VideoCodec.findByName(value);
|
|
if (videoCodec == null) {
|
|
throw new IllegalArgumentException("Video codec " + value + " not supported");
|
|
}
|
|
options.videoCodec = videoCodec;
|
|
break;
|
|
case "audio_codec":
|
|
AudioCodec audioCodec = AudioCodec.findByName(value);
|
|
if (audioCodec == null) {
|
|
throw new IllegalArgumentException("Audio codec " + value + " not supported");
|
|
}
|
|
options.audioCodec = audioCodec;
|
|
break;
|
|
case "audio_source":
|
|
AudioSource audioSource = AudioSource.findByName(value);
|
|
if (audioSource == null) {
|
|
throw new IllegalArgumentException("Audio source " + value + " not supported");
|
|
}
|
|
options.audioSource = audioSource;
|
|
break;
|
|
case "max_size":
|
|
options.maxSize = Integer.parseInt(value) & ~7; // multiple of 8
|
|
break;
|
|
case "video_bit_rate":
|
|
options.videoBitRate = Integer.parseInt(value);
|
|
break;
|
|
case "audio_bit_rate":
|
|
options.audioBitRate = Integer.parseInt(value);
|
|
break;
|
|
case "max_fps":
|
|
options.maxFps = Integer.parseInt(value);
|
|
break;
|
|
case "lock_video_orientation":
|
|
options.lockVideoOrientation = Integer.parseInt(value);
|
|
break;
|
|
case "tunnel_forward":
|
|
options.tunnelForward = Boolean.parseBoolean(value);
|
|
break;
|
|
case "crop":
|
|
options.crop = parseCrop(value);
|
|
break;
|
|
case "control":
|
|
options.control = Boolean.parseBoolean(value);
|
|
break;
|
|
case "display_id":
|
|
options.displayId = Integer.parseInt(value);
|
|
break;
|
|
case "show_touches":
|
|
options.showTouches = Boolean.parseBoolean(value);
|
|
break;
|
|
case "stay_awake":
|
|
options.stayAwake = Boolean.parseBoolean(value);
|
|
break;
|
|
case "video_codec_options":
|
|
options.videoCodecOptions = CodecOption.parse(value);
|
|
break;
|
|
case "audio_codec_options":
|
|
options.audioCodecOptions = CodecOption.parse(value);
|
|
break;
|
|
case "video_encoder":
|
|
if (!value.isEmpty()) {
|
|
options.videoEncoder = value;
|
|
}
|
|
break;
|
|
case "audio_encoder":
|
|
if (!value.isEmpty()) {
|
|
options.audioEncoder = value;
|
|
}
|
|
case "power_off_on_close":
|
|
options.powerOffScreenOnClose = Boolean.parseBoolean(value);
|
|
break;
|
|
case "clipboard_autosync":
|
|
options.clipboardAutosync = Boolean.parseBoolean(value);
|
|
break;
|
|
case "downsize_on_error":
|
|
options.downsizeOnError = Boolean.parseBoolean(value);
|
|
break;
|
|
case "cleanup":
|
|
options.cleanup = Boolean.parseBoolean(value);
|
|
break;
|
|
case "power_on":
|
|
options.powerOn = Boolean.parseBoolean(value);
|
|
break;
|
|
case "list_encoders":
|
|
options.listEncoders = Boolean.parseBoolean(value);
|
|
break;
|
|
case "list_displays":
|
|
options.listDisplays = Boolean.parseBoolean(value);
|
|
break;
|
|
case "send_device_meta":
|
|
options.sendDeviceMeta = Boolean.parseBoolean(value);
|
|
break;
|
|
case "send_frame_meta":
|
|
options.sendFrameMeta = Boolean.parseBoolean(value);
|
|
break;
|
|
case "send_dummy_byte":
|
|
options.sendDummyByte = Boolean.parseBoolean(value);
|
|
break;
|
|
case "send_codec_meta":
|
|
options.sendCodecMeta = Boolean.parseBoolean(value);
|
|
break;
|
|
case "raw_video_stream":
|
|
boolean rawVideoStream = Boolean.parseBoolean(value);
|
|
if (rawVideoStream) {
|
|
options.sendDeviceMeta = false;
|
|
options.sendFrameMeta = false;
|
|
options.sendDummyByte = false;
|
|
options.sendCodecMeta = false;
|
|
}
|
|
break;
|
|
default:
|
|
Ln.w("Unknown server option: " + key);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
private static Rect parseCrop(String crop) {
|
|
if (crop.isEmpty()) {
|
|
return null;
|
|
}
|
|
// input format: "width:height:x:y"
|
|
String[] tokens = crop.split(":");
|
|
if (tokens.length != 4) {
|
|
throw new IllegalArgumentException("Crop must contains 4 values separated by colons: \"" + crop + "\"");
|
|
}
|
|
int width = Integer.parseInt(tokens[0]);
|
|
int height = Integer.parseInt(tokens[1]);
|
|
int x = Integer.parseInt(tokens[2]);
|
|
int y = Integer.parseInt(tokens[3]);
|
|
return new Rect(x, y, x + width, y + height);
|
|
}
|
|
}
|