Do not print stacktraces when unnecessary

User-friendly error messages are printed on specific configuration
exceptions. In that case, do not print the stacktrace.

Also handle the user-friendly error message directly where the error
occurs, and print multiline messages in a single log call, to avoid
confusing interleaving.
This commit is contained in:
Romain Vimont 2023-02-19 19:36:46 +01:00
parent 9f8e96e895
commit b43938fa66
6 changed files with 44 additions and 77 deletions

View file

@ -0,0 +1,7 @@
package com.genymobile.scrcpy;
public class ConfigurationException extends Exception {
public ConfigurationException(String message) {
super(message);
}
}

View file

@ -61,12 +61,12 @@ public final class Device {
private final boolean supportsInputEvents; private final boolean supportsInputEvents;
public Device(Options options) { public Device(Options options) throws ConfigurationException {
displayId = options.getDisplayId(); displayId = options.getDisplayId();
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId); DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
if (displayInfo == null) { if (displayInfo == null) {
int[] displayIds = ServiceManager.getDisplayManager().getDisplayIds(); Ln.e(buildUnknownDisplayIdMessage(displayId));
throw new InvalidDisplayIdException(displayId, displayIds); throw new ConfigurationException("Unknown display id: " + displayId);
} }
int displayInfoFlags = displayInfo.getFlags(); int displayInfoFlags = displayInfo.getFlags();
@ -130,6 +130,18 @@ public final class Device {
} }
} }
private static String buildUnknownDisplayIdMessage(int displayId) {
StringBuilder msg = new StringBuilder("Display ").append(displayId).append(" not found");
int[] displayIds = ServiceManager.getDisplayManager().getDisplayIds();
if (displayIds != null && displayIds.length > 0) {
msg.append("\nTry to use one of the available display ids:");
for (int id : displayIds) {
msg.append("\n scrcpy --display=").append(id);
}
}
return msg.toString();
}
public synchronized void setMaxSize(int newMaxSize) { public synchronized void setMaxSize(int newMaxSize) {
maxSize = newMaxSize; maxSize = newMaxSize;
screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation); screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation);

View file

@ -1,21 +0,0 @@
package com.genymobile.scrcpy;
public class InvalidDisplayIdException extends RuntimeException {
private final int displayId;
private final int[] availableDisplayIds;
public InvalidDisplayIdException(int displayId, int[] availableDisplayIds) {
super("There is no display having id " + displayId);
this.displayId = displayId;
this.availableDisplayIds = availableDisplayIds;
}
public int getDisplayId() {
return displayId;
}
public int[] getAvailableDisplayIds() {
return availableDisplayIds;
}
}

View file

@ -1,23 +0,0 @@
package com.genymobile.scrcpy;
import android.media.MediaCodecInfo;
public class InvalidEncoderException extends RuntimeException {
private final String name;
private final MediaCodecInfo[] availableEncoders;
public InvalidEncoderException(String name, MediaCodecInfo[] availableEncoders) {
super("There is no encoder having name '" + name + "'");
this.name = name;
this.availableEncoders = availableEncoders;
}
public String getName() {
return name;
}
public MediaCodecInfo[] getAvailableEncoders() {
return availableEncoders;
}
}

View file

@ -63,7 +63,7 @@ public class ScreenEncoder implements Device.RotationListener {
return rotationChanged.getAndSet(false); return rotationChanged.getAndSet(false);
} }
public void streamScreen(Device device, Callbacks callbacks) throws IOException { public void streamScreen(Device device, Callbacks callbacks) throws IOException, ConfigurationException {
MediaCodec codec = createCodec(videoMimeType, encoderName); MediaCodec codec = createCodec(videoMimeType, encoderName);
MediaFormat format = createFormat(videoMimeType, bitRate, maxFps, codecOptions); MediaFormat format = createFormat(videoMimeType, bitRate, maxFps, codecOptions);
IBinder display = createDisplay(); IBinder display = createDisplay();
@ -207,14 +207,14 @@ public class ScreenEncoder implements Device.RotationListener {
return result.toArray(new MediaCodecInfo[result.size()]); return result.toArray(new MediaCodecInfo[result.size()]);
} }
private static MediaCodec createCodec(String videoMimeType, String encoderName) throws IOException { private static MediaCodec createCodec(String videoMimeType, String encoderName) throws IOException, ConfigurationException {
if (encoderName != null) { if (encoderName != null) {
Ln.d("Creating encoder by name: '" + encoderName + "'"); Ln.d("Creating encoder by name: '" + encoderName + "'");
try { try {
return MediaCodec.createByCodecName(encoderName); return MediaCodec.createByCodecName(encoderName);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
MediaCodecInfo[] encoders = listEncoders(videoMimeType); Ln.e(buildUnknownEncoderMessage(videoMimeType, encoderName));
throw new InvalidEncoderException(encoderName, encoders); throw new ConfigurationException("Unknown encoder: " + encoderName);
} }
} }
MediaCodec codec = MediaCodec.createEncoderByType(videoMimeType); MediaCodec codec = MediaCodec.createEncoderByType(videoMimeType);
@ -222,6 +222,18 @@ public class ScreenEncoder implements Device.RotationListener {
return codec; return codec;
} }
private static String buildUnknownEncoderMessage(String videoMimeType, String encoderName) {
StringBuilder msg = new StringBuilder("Encoder '").append(encoderName).append("' not found");
MediaCodecInfo[] encoders = listEncoders(videoMimeType);
if (encoders != null && encoders.length > 0) {
msg.append("\nTry to use one of the available encoders:");
for (MediaCodecInfo encoder : encoders) {
msg.append("\n scrcpy --encoder='").append(encoder.getName()).append("'");
}
}
return msg.toString();
}
private static void setCodecOption(MediaFormat format, CodecOption codecOption) { private static void setCodecOption(MediaFormat format, CodecOption codecOption) {
String key = codecOption.getKey(); String key = codecOption.getKey();
Object value = codecOption.getValue(); Object value = codecOption.getValue();

View file

@ -1,7 +1,6 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import android.graphics.Rect; import android.graphics.Rect;
import android.media.MediaCodecInfo;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.Build; import android.os.Build;
@ -59,7 +58,7 @@ public final class Server {
} }
} }
private static void scrcpy(Options options) throws IOException { private static void scrcpy(Options options) throws IOException, ConfigurationException {
Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")"); Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
final Device device = new Device(options); final Device device = new Device(options);
List<CodecOption> codecOptions = options.getCodecOptions(); List<CodecOption> codecOptions = options.getCodecOptions();
@ -299,38 +298,19 @@ public final class Server {
return new Rect(x, y, x + width, y + height); return new Rect(x, y, x + width, y + height);
} }
private static void suggestFix(Throwable e) {
if (e instanceof InvalidDisplayIdException) {
InvalidDisplayIdException idie = (InvalidDisplayIdException) e;
int[] displayIds = idie.getAvailableDisplayIds();
if (displayIds != null && displayIds.length > 0) {
Ln.e("Try to use one of the available display ids:");
for (int id : displayIds) {
Ln.e(" scrcpy --display=" + id);
}
}
} else if (e instanceof InvalidEncoderException) {
InvalidEncoderException iee = (InvalidEncoderException) e;
MediaCodecInfo[] encoders = iee.getAvailableEncoders();
if (encoders != null && encoders.length > 0) {
Ln.e("Try to use one of the available encoders:");
for (MediaCodecInfo encoder : encoders) {
Ln.e(" scrcpy --encoder='" + encoder.getName() + "'");
}
}
}
}
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
Thread.setDefaultUncaughtExceptionHandler((t, e) -> { Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
Ln.e("Exception on thread " + t, e); Ln.e("Exception on thread " + t, e);
suggestFix(e);
}); });
Options options = createOptions(args); Options options = createOptions(args);
Ln.initLogLevel(options.getLogLevel()); Ln.initLogLevel(options.getLogLevel());
try {
scrcpy(options); scrcpy(options);
} catch (ConfigurationException e) {
// Do not print stack trace, a user-friendly error-message has already been logged
}
} }
} }