mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-01 15:25:59 +00:00
WIP: Resolution dialog
This commit is contained in:
parent
00415aac79
commit
41592b2ef4
@ -220,7 +220,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Enter landscape unless we're on a square screen
|
// Enter landscape unless we're on a square screen
|
||||||
setPreferredOrientationForCurrentDisplay();
|
setPreferredOrientationForCurrentDisplay();
|
||||||
|
|
||||||
if (prefConfig.stretchVideo || shouldIgnoreInsetsForResolution(prefConfig.width, prefConfig.height)) {
|
if (prefConfig.stretchVideo || prefConfig.resolution.fullScreen) {
|
||||||
// Allow the activity to layout under notches if the fill-screen option
|
// Allow the activity to layout under notches if the fill-screen option
|
||||||
// was turned on by the user or it's a full-screen native resolution
|
// was turned on by the user or it's a full-screen native resolution
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
@ -456,7 +456,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
StreamConfiguration config = new StreamConfiguration.Builder()
|
StreamConfiguration config = new StreamConfiguration.Builder()
|
||||||
.setResolution(prefConfig.width, prefConfig.height)
|
.setResolution(prefConfig.resolution.width, prefConfig.resolution.height)
|
||||||
.setLaunchRefreshRate(prefConfig.fps)
|
.setLaunchRefreshRate(prefConfig.fps)
|
||||||
.setRefreshRate(chosenFrameRate)
|
.setRefreshRate(chosenFrameRate)
|
||||||
.setApp(new NvApp(appName != null ? appName : "app", appId, appSupportsHdr))
|
.setApp(new NvApp(appName != null ? appName : "app", appId, appSupportsHdr))
|
||||||
@ -551,12 +551,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For native resolution, we will lock the orientation to the one that matches the specified resolution
|
// For native resolution, we will lock the orientation to the one that matches the specified resolution
|
||||||
if (PreferenceConfiguration.isNativeResolution(prefConfig.width, prefConfig.height)) {
|
if (prefConfig.resolution.type == PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||||
if (prefConfig.width > prefConfig.height) {
|
if (prefConfig.resolution.portrait) {
|
||||||
desiredOrientation = Configuration.ORIENTATION_LANDSCAPE;
|
desiredOrientation = Configuration.ORIENTATION_PORTRAIT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
desiredOrientation = Configuration.ORIENTATION_PORTRAIT;
|
desiredOrientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,7 +649,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
private PictureInPictureParams getPictureInPictureParams(boolean autoEnter) {
|
private PictureInPictureParams getPictureInPictureParams(boolean autoEnter) {
|
||||||
PictureInPictureParams.Builder builder =
|
PictureInPictureParams.Builder builder =
|
||||||
new PictureInPictureParams.Builder()
|
new PictureInPictureParams.Builder()
|
||||||
.setAspectRatio(new Rational(prefConfig.width, prefConfig.height))
|
.setAspectRatio(new Rational(prefConfig.resolution.width, prefConfig.resolution.height))
|
||||||
.setSourceRectHint(new Rect(
|
.setSourceRectHint(new Rect(
|
||||||
streamView.getLeft(), streamView.getTop(),
|
streamView.getLeft(), streamView.getTop(),
|
||||||
streamView.getRight(), streamView.getBottom()));
|
streamView.getRight(), streamView.getBottom()));
|
||||||
@ -771,26 +771,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
Math.round(refreshRate) % prefConfig.fps <= 3;
|
Math.round(refreshRate) % prefConfig.fps <= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldIgnoreInsetsForResolution(int width, int height) {
|
|
||||||
// Never ignore insets for non-native resolutions
|
|
||||||
if (!PreferenceConfiguration.isNativeResolution(width, height)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
Display display = getWindowManager().getDefaultDisplay();
|
|
||||||
for (Display.Mode candidate : display.getSupportedModes()) {
|
|
||||||
// Ignore insets if this is an exact match for the display resolution
|
|
||||||
if ((width == candidate.getPhysicalWidth() && height == candidate.getPhysicalHeight()) ||
|
|
||||||
(height == candidate.getPhysicalWidth() && width == candidate.getPhysicalHeight())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean mayReduceRefreshRate() {
|
private boolean mayReduceRefreshRate() {
|
||||||
return prefConfig.framePacing == PreferenceConfiguration.FRAME_PACING_CAP_FPS ||
|
return prefConfig.framePacing == PreferenceConfiguration.FRAME_PACING_CAP_FPS ||
|
||||||
prefConfig.framePacing == PreferenceConfiguration.FRAME_PACING_MAX_SMOOTHNESS ||
|
prefConfig.framePacing == PreferenceConfiguration.FRAME_PACING_MAX_SMOOTHNESS ||
|
||||||
@ -805,7 +785,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// On M, we can explicitly set the optimal display mode
|
// On M, we can explicitly set the optimal display mode
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
Display.Mode bestMode = display.getMode();
|
Display.Mode bestMode = display.getMode();
|
||||||
boolean isNativeResolutionStream = PreferenceConfiguration.isNativeResolution(prefConfig.width, prefConfig.height);
|
|
||||||
boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate());
|
boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate());
|
||||||
boolean refreshRateIsEqual = isRefreshRateEqualMatch(bestMode.getRefreshRate());
|
boolean refreshRateIsEqual = isRefreshRateEqualMatch(bestMode.getRefreshRate());
|
||||||
|
|
||||||
@ -813,13 +792,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate();
|
boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate();
|
||||||
boolean resolutionReduced = candidate.getPhysicalWidth() < bestMode.getPhysicalWidth() ||
|
boolean resolutionReduced = candidate.getPhysicalWidth() < bestMode.getPhysicalWidth() ||
|
||||||
candidate.getPhysicalHeight() < bestMode.getPhysicalHeight();
|
candidate.getPhysicalHeight() < bestMode.getPhysicalHeight();
|
||||||
boolean resolutionFitsStream = candidate.getPhysicalWidth() >= prefConfig.width &&
|
boolean resolutionFitsStream = candidate.getPhysicalWidth() >= prefConfig.resolution.width &&
|
||||||
candidate.getPhysicalHeight() >= prefConfig.height;
|
candidate.getPhysicalHeight() >= prefConfig.resolution.height;
|
||||||
|
|
||||||
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
|
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
|
||||||
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
|
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
|
||||||
|
|
||||||
if (candidate.getPhysicalWidth() > 4096 && prefConfig.width <= 4096) {
|
if (candidate.getPhysicalWidth() > 4096 && prefConfig.resolution.width <= 4096) {
|
||||||
// Avoid resolutions options above 4K to be safe
|
// Avoid resolutions options above 4K to be safe
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -827,7 +806,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// On non-4K streams, we force the resolution to never change unless it's above
|
// On non-4K streams, we force the resolution to never change unless it's above
|
||||||
// 60 FPS, which may require a resolution reduction due to HDMI bandwidth limitations,
|
// 60 FPS, which may require a resolution reduction due to HDMI bandwidth limitations,
|
||||||
// or it's a native resolution stream.
|
// or it's a native resolution stream.
|
||||||
if (prefConfig.width < 3840 && prefConfig.fps <= 60 && !isNativeResolutionStream) {
|
if (prefConfig.resolution.width < 3840 && prefConfig.fps <= 60 &&
|
||||||
|
prefConfig.resolution.type != PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||||
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
|
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
|
||||||
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
|
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
|
||||||
continue;
|
continue;
|
||||||
@ -940,7 +920,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
display.getSize(screenSize);
|
display.getSize(screenSize);
|
||||||
|
|
||||||
double screenAspectRatio = ((double)screenSize.y) / screenSize.x;
|
double screenAspectRatio = ((double)screenSize.y) / screenSize.x;
|
||||||
double streamAspectRatio = ((double)prefConfig.height) / prefConfig.width;
|
double streamAspectRatio = ((double)prefConfig.resolution.height) / prefConfig.resolution.width;
|
||||||
if (Math.abs(screenAspectRatio - streamAspectRatio) < 0.001) {
|
if (Math.abs(screenAspectRatio - streamAspectRatio) < 0.001) {
|
||||||
LimeLog.info("Stream has compatible aspect ratio with output display");
|
LimeLog.info("Stream has compatible aspect ratio with output display");
|
||||||
aspectRatioMatch = true;
|
aspectRatioMatch = true;
|
||||||
@ -949,11 +929,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
if (prefConfig.stretchVideo || aspectRatioMatch) {
|
if (prefConfig.stretchVideo || aspectRatioMatch) {
|
||||||
// Set the surface to the size of the video
|
// Set the surface to the size of the video
|
||||||
streamView.getHolder().setFixedSize(prefConfig.width, prefConfig.height);
|
streamView.getHolder().setFixedSize(prefConfig.resolution.width, prefConfig.resolution.height);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Set the surface to scale based on the aspect ratio of the stream
|
// Set the surface to scale based on the aspect ratio of the stream
|
||||||
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
streamView.setDesiredAspectRatio((double)prefConfig.resolution.width / (double)prefConfig.resolution.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) ||
|
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) ||
|
||||||
|
@ -134,7 +134,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
private boolean decoderCanMeetPerformancePoint(MediaCodecInfo.VideoCapabilities caps, PreferenceConfiguration prefs) {
|
private boolean decoderCanMeetPerformancePoint(MediaCodecInfo.VideoCapabilities caps, PreferenceConfiguration prefs) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
MediaCodecInfo.VideoCapabilities.PerformancePoint targetPerfPoint = new MediaCodecInfo.VideoCapabilities.PerformancePoint(prefs.width, prefs.height, prefs.fps);
|
MediaCodecInfo.VideoCapabilities.PerformancePoint targetPerfPoint = new MediaCodecInfo.VideoCapabilities.PerformancePoint(prefs.resolution.width, prefs.resolution.height, prefs.fps);
|
||||||
List<MediaCodecInfo.VideoCapabilities.PerformancePoint> perfPoints = caps.getSupportedPerformancePoints();
|
List<MediaCodecInfo.VideoCapabilities.PerformancePoint> perfPoints = caps.getSupportedPerformancePoints();
|
||||||
if (perfPoints != null) {
|
if (perfPoints != null) {
|
||||||
for (MediaCodecInfo.VideoCapabilities.PerformancePoint perfPoint : perfPoints) {
|
for (MediaCodecInfo.VideoCapabilities.PerformancePoint perfPoint : perfPoints) {
|
||||||
@ -155,7 +155,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|||||||
try {
|
try {
|
||||||
// We'll ask the decoder what it can do for us at this resolution and see if our
|
// We'll ask the decoder what it can do for us at this resolution and see if our
|
||||||
// requested frame rate falls below or inside the range of achievable frame rates.
|
// requested frame rate falls below or inside the range of achievable frame rates.
|
||||||
Range<Double> fpsRange = caps.getAchievableFrameRatesFor(prefs.width, prefs.height);
|
Range<Double> fpsRange = caps.getAchievableFrameRatesFor(prefs.resolution.width, prefs.resolution.height);
|
||||||
if (fpsRange != null) {
|
if (fpsRange != null) {
|
||||||
return prefs.fps <= fpsRange.getUpper();
|
return prefs.fps <= fpsRange.getUpper();
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|||||||
// As a last resort, we will use areSizeAndRateSupported() which is explicitly NOT a
|
// As a last resort, we will use areSizeAndRateSupported() which is explicitly NOT a
|
||||||
// performance metric, but it can work at least for the purpose of determining if
|
// performance metric, but it can work at least for the purpose of determining if
|
||||||
// the codec is going to die when given a stream with the specified settings.
|
// the codec is going to die when given a stream with the specified settings.
|
||||||
return caps.areSizeAndRateSupported(prefs.width, prefs.height, prefs.fps);
|
return caps.areSizeAndRateSupported(prefs.resolution.width, prefs.resolution.height, prefs.fps);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean decoderCanMeetPerformancePointWithHevcAndNotAvc(MediaCodecInfo avcDecoderInfo, MediaCodecInfo hevcDecoderInfo, PreferenceConfiguration prefs) {
|
private boolean decoderCanMeetPerformancePointWithHevcAndNotAvc(MediaCodecInfo avcDecoderInfo, MediaCodecInfo hevcDecoderInfo, PreferenceConfiguration prefs) {
|
||||||
@ -211,7 +211,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|||||||
LimeLog.info("Forcing HEVC enabled for HDR streaming");
|
LimeLog.info("Forcing HEVC enabled for HDR streaming");
|
||||||
}
|
}
|
||||||
// > 4K streaming also requires HEVC, so force it on there too.
|
// > 4K streaming also requires HEVC, so force it on there too.
|
||||||
else if (prefs.width > 4096 || prefs.height > 4096) {
|
else if (prefs.resolution.width > 4096 || prefs.resolution.height > 4096) {
|
||||||
LimeLog.info("Forcing HEVC enabled for over 4K streaming");
|
LimeLog.info("Forcing HEVC enabled for over 4K streaming");
|
||||||
}
|
}
|
||||||
// Use HEVC if the H.264 decoder is unable to meet the performance point
|
// Use HEVC if the H.264 decoder is unable to meet the performance point
|
||||||
@ -271,7 +271,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
|||||||
int hevcOptimalSlicesPerFrame = 0;
|
int hevcOptimalSlicesPerFrame = 0;
|
||||||
if (avcDecoder != null) {
|
if (avcDecoder != null) {
|
||||||
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
||||||
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName(), prefs.height);
|
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName(), prefs.resolution.height);
|
||||||
avcOptimalSlicesPerFrame = MediaCodecHelper.getDecoderOptimalSlicesPerFrame(avcDecoder.getName());
|
avcOptimalSlicesPerFrame = MediaCodecHelper.getDecoderOptimalSlicesPerFrame(avcDecoder.getName());
|
||||||
|
|
||||||
if (directSubmit) {
|
if (directSubmit) {
|
||||||
|
@ -90,15 +90,15 @@ public class PreferenceConfiguration {
|
|||||||
public static final int FRAME_PACING_CAP_FPS = 2;
|
public static final int FRAME_PACING_CAP_FPS = 2;
|
||||||
public static final int FRAME_PACING_MAX_SMOOTHNESS = 3;
|
public static final int FRAME_PACING_MAX_SMOOTHNESS = 3;
|
||||||
|
|
||||||
public static final String RES_360P = "640x360";
|
public static final String RES_360P = "s_640x360";
|
||||||
public static final String RES_480P = "854x480";
|
public static final String RES_480P = "s_854x480";
|
||||||
public static final String RES_720P = "1280x720";
|
public static final String RES_720P = "s_1280x720";
|
||||||
public static final String RES_1080P = "1920x1080";
|
public static final String RES_1080P = "s_1920x1080";
|
||||||
public static final String RES_1440P = "2560x1440";
|
public static final String RES_1440P = "s_2560x1440";
|
||||||
public static final String RES_4K = "3840x2160";
|
public static final String RES_4K = "s_3840x2160";
|
||||||
public static final String RES_NATIVE = "Native";
|
|
||||||
|
|
||||||
public int width, height, fps;
|
public StreamResolution resolution;
|
||||||
|
public int fps;
|
||||||
public int bitrate;
|
public int bitrate;
|
||||||
public int videoFormat;
|
public int videoFormat;
|
||||||
public int deadzonePercentage;
|
public int deadzonePercentage;
|
||||||
@ -125,7 +125,9 @@ public class PreferenceConfiguration {
|
|||||||
public boolean enableAudioFx;
|
public boolean enableAudioFx;
|
||||||
public boolean reduceRefreshRate;
|
public boolean reduceRefreshRate;
|
||||||
|
|
||||||
public static boolean isNativeResolution(int width, int height) {
|
// This is used only for migration to the new type code format of resolution strings
|
||||||
|
// It doesn't properly handle custom resolutions!
|
||||||
|
private static boolean legacyIsNativeResolution(int width, int height) {
|
||||||
// It's not a native resolution if it matches an existing resolution option
|
// It's not a native resolution if it matches an existing resolution option
|
||||||
if (width == 640 && height == 360) {
|
if (width == 640 && height == 360) {
|
||||||
return false;
|
return false;
|
||||||
@ -175,6 +177,8 @@ public class PreferenceConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String convertFromLegacyResolutionString(String resString) {
|
private static String convertFromLegacyResolutionString(String resString) {
|
||||||
|
if (!resString.contains("x")) {
|
||||||
|
// Convert from hardcoded strings to strings with dimensions embedded
|
||||||
if (resString.equalsIgnoreCase("360p")) {
|
if (resString.equalsIgnoreCase("360p")) {
|
||||||
return RES_360P;
|
return RES_360P;
|
||||||
}
|
}
|
||||||
@ -198,36 +202,22 @@ public class PreferenceConfiguration {
|
|||||||
return RES_720P;
|
return RES_720P;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (Character.isDigit(resString.charAt(0))) {
|
||||||
private static int getWidthFromResolutionString(String resString) {
|
// Convert to type code prefix format
|
||||||
return Integer.parseInt(resString.split("x")[0]);
|
String[] dims = resString.split("x");
|
||||||
|
int width = Integer.parseInt(dims[0]);
|
||||||
|
int height = Integer.parseInt(dims[1]);
|
||||||
|
return new StreamResolution(width, height,
|
||||||
|
legacyIsNativeResolution(width, height) ? StreamResolution.ResolutionType.NATIVE : StreamResolution.ResolutionType.STANDARD)
|
||||||
|
.toPrefString();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
private static int getHeightFromResolutionString(String resString) {
|
return resString;
|
||||||
return Integer.parseInt(resString.split("x")[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getResolutionString(int width, int height) {
|
|
||||||
switch (height) {
|
|
||||||
case 360:
|
|
||||||
return RES_360P;
|
|
||||||
case 480:
|
|
||||||
return RES_480P;
|
|
||||||
default:
|
|
||||||
case 720:
|
|
||||||
return RES_720P;
|
|
||||||
case 1080:
|
|
||||||
return RES_1080P;
|
|
||||||
case 1440:
|
|
||||||
return RES_1440P;
|
|
||||||
case 2160:
|
|
||||||
return RES_4K;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getDefaultBitrate(String resString, String fpsString) {
|
public static int getDefaultBitrate(String resString, String fpsString) {
|
||||||
int width = getWidthFromResolutionString(resString);
|
StreamResolution res = new StreamResolution(resString);
|
||||||
int height = getHeightFromResolutionString(resString);
|
|
||||||
int fps = Integer.parseInt(fpsString);
|
int fps = Integer.parseInt(fpsString);
|
||||||
|
|
||||||
// This table prefers 16:10 resolutions because they are
|
// This table prefers 16:10 resolutions because they are
|
||||||
@ -238,23 +228,23 @@ public class PreferenceConfiguration {
|
|||||||
// This logic is shamelessly stolen from Moonlight Qt:
|
// This logic is shamelessly stolen from Moonlight Qt:
|
||||||
// https://github.com/moonlight-stream/moonlight-qt/blob/master/app/settings/streamingpreferences.cpp
|
// https://github.com/moonlight-stream/moonlight-qt/blob/master/app/settings/streamingpreferences.cpp
|
||||||
|
|
||||||
if (width * height <= 640 * 360) {
|
if (res.width * res.height <= 640 * 360) {
|
||||||
return (int)(1000 * (fps / 30.0));
|
return (int)(1000 * (fps / 30.0));
|
||||||
}
|
}
|
||||||
else if (width * height <= 854 * 480) {
|
else if (res.width * res.height <= 854 * 480) {
|
||||||
return (int)(1500 * (fps / 30.0));
|
return (int)(1500 * (fps / 30.0));
|
||||||
}
|
}
|
||||||
// This covers 1280x720 and 1280x800 too
|
// This covers 1280x720 and 1280x800 too
|
||||||
else if (width * height <= 1366 * 768) {
|
else if (res.width * res.height <= 1366 * 768) {
|
||||||
return (int)(5000 * (fps / 30.0));
|
return (int)(5000 * (fps / 30.0));
|
||||||
}
|
}
|
||||||
else if (width * height <= 1920 * 1200) {
|
else if (res.width * res.height <= 1920 * 1200) {
|
||||||
return (int)(10000 * (fps / 30.0));
|
return (int)(10000 * (fps / 30.0));
|
||||||
}
|
}
|
||||||
else if (width * height <= 2560 * 1600) {
|
else if (res.width * res.height <= 2560 * 1600) {
|
||||||
return (int)(20000 * (fps / 30.0));
|
return (int)(20000 * (fps / 30.0));
|
||||||
}
|
}
|
||||||
else /* if (width * height <= 3840 * 2160) */ {
|
else /* if (res.width * res.height <= 3840 * 2160) */ {
|
||||||
return (int)(40000 * (fps / 30.0));
|
return (int)(40000 * (fps / 30.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,55 +371,46 @@ public class PreferenceConfiguration {
|
|||||||
String str = prefs.getString(LEGACY_RES_FPS_PREF_STRING, null);
|
String str = prefs.getString(LEGACY_RES_FPS_PREF_STRING, null);
|
||||||
if (str != null) {
|
if (str != null) {
|
||||||
if (str.equals("360p30")) {
|
if (str.equals("360p30")) {
|
||||||
config.width = 640;
|
config.resolution = new StreamResolution(640, 360, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 360;
|
|
||||||
config.fps = 30;
|
config.fps = 30;
|
||||||
}
|
}
|
||||||
else if (str.equals("360p60")) {
|
else if (str.equals("360p60")) {
|
||||||
config.width = 640;
|
config.resolution = new StreamResolution(640, 360, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 360;
|
|
||||||
config.fps = 60;
|
config.fps = 60;
|
||||||
}
|
}
|
||||||
else if (str.equals("720p30")) {
|
else if (str.equals("720p30")) {
|
||||||
config.width = 1280;
|
config.resolution = new StreamResolution(1280, 720, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 720;
|
|
||||||
config.fps = 30;
|
config.fps = 30;
|
||||||
}
|
}
|
||||||
else if (str.equals("720p60")) {
|
else if (str.equals("720p60")) {
|
||||||
config.width = 1280;
|
config.resolution = new StreamResolution(1280, 720, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 720;
|
|
||||||
config.fps = 60;
|
config.fps = 60;
|
||||||
}
|
}
|
||||||
else if (str.equals("1080p30")) {
|
else if (str.equals("1080p30")) {
|
||||||
config.width = 1920;
|
config.resolution = new StreamResolution(1920, 1080, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 1080;
|
|
||||||
config.fps = 30;
|
config.fps = 30;
|
||||||
}
|
}
|
||||||
else if (str.equals("1080p60")) {
|
else if (str.equals("1080p60")) {
|
||||||
config.width = 1920;
|
config.resolution = new StreamResolution(1920, 1080, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 1080;
|
|
||||||
config.fps = 60;
|
config.fps = 60;
|
||||||
}
|
}
|
||||||
else if (str.equals("4K30")) {
|
else if (str.equals("4K30")) {
|
||||||
config.width = 3840;
|
config.resolution = new StreamResolution(3840, 2160, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 2160;
|
|
||||||
config.fps = 30;
|
config.fps = 30;
|
||||||
}
|
}
|
||||||
else if (str.equals("4K60")) {
|
else if (str.equals("4K60")) {
|
||||||
config.width = 3840;
|
config.resolution = new StreamResolution(3840, 2160, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 2160;
|
|
||||||
config.fps = 60;
|
config.fps = 60;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Should never get here
|
// Should never get here
|
||||||
config.width = 1280;
|
config.resolution = new StreamResolution(1280, 720, StreamResolution.ResolutionType.STANDARD);
|
||||||
config.height = 720;
|
|
||||||
config.fps = 60;
|
config.fps = 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.remove(LEGACY_RES_FPS_PREF_STRING)
|
.remove(LEGACY_RES_FPS_PREF_STRING)
|
||||||
.putString(RESOLUTION_PREF_STRING, getResolutionString(config.width, config.height))
|
.putString(RESOLUTION_PREF_STRING, config.resolution.toPrefString())
|
||||||
.putString(FPS_PREF_STRING, ""+config.fps)
|
.putString(FPS_PREF_STRING, ""+config.fps)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
@ -438,13 +419,12 @@ public class PreferenceConfiguration {
|
|||||||
String resStr = prefs.getString(RESOLUTION_PREF_STRING, PreferenceConfiguration.DEFAULT_RESOLUTION);
|
String resStr = prefs.getString(RESOLUTION_PREF_STRING, PreferenceConfiguration.DEFAULT_RESOLUTION);
|
||||||
|
|
||||||
// Convert legacy resolution strings to the new style
|
// Convert legacy resolution strings to the new style
|
||||||
if (!resStr.contains("x")) {
|
if (Character.isDigit(resStr.charAt(0)) || !resStr.contains("x")) {
|
||||||
resStr = PreferenceConfiguration.convertFromLegacyResolutionString(resStr);
|
resStr = PreferenceConfiguration.convertFromLegacyResolutionString(resStr);
|
||||||
prefs.edit().putString(RESOLUTION_PREF_STRING, resStr).apply();
|
prefs.edit().putString(RESOLUTION_PREF_STRING, resStr).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
config.width = PreferenceConfiguration.getWidthFromResolutionString(resStr);
|
config.resolution = new StreamResolution(resStr);
|
||||||
config.height = PreferenceConfiguration.getHeightFromResolutionString(resStr);
|
|
||||||
config.fps = Integer.parseInt(prefs.getString(FPS_PREF_STRING, PreferenceConfiguration.DEFAULT_FPS));
|
config.fps = Integer.parseInt(prefs.getString(FPS_PREF_STRING, PreferenceConfiguration.DEFAULT_FPS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,4 +488,112 @@ public class PreferenceConfiguration {
|
|||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class StreamResolution {
|
||||||
|
public int width;
|
||||||
|
public int height;
|
||||||
|
public ResolutionType type;
|
||||||
|
public boolean portrait;
|
||||||
|
public boolean fullScreen;
|
||||||
|
|
||||||
|
private static final char RES_START_DELIMITER = '_';
|
||||||
|
private static final char RES_DIM_DELIMITER = 'x';
|
||||||
|
|
||||||
|
private static final char TYPE_CODE_STANDARD = 's';
|
||||||
|
private static final char TYPE_CODE_NATIVE = 'n';
|
||||||
|
|
||||||
|
private static final char OPTION_CODE_PORTRAIT = 'p';
|
||||||
|
private static final char OPTION_CODE_FULL_SCREEN = 'f';
|
||||||
|
|
||||||
|
public enum ResolutionType {
|
||||||
|
STANDARD,
|
||||||
|
NATIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
private StreamResolution(int width, int height, ResolutionType type) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
|
// Just guess for the options, since we don't know for sure
|
||||||
|
this.portrait = height > width;
|
||||||
|
this.fullScreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamResolution(int width, int height, ResolutionType type, boolean portrait, boolean fullScreen) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.type = type;
|
||||||
|
this.portrait = portrait;
|
||||||
|
this.fullScreen = fullScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamResolution(String resString) {
|
||||||
|
// Type code is the first character in the string
|
||||||
|
switch (resString.charAt(0)) {
|
||||||
|
case TYPE_CODE_NATIVE:
|
||||||
|
this.type = StreamResolution.ResolutionType.NATIVE;
|
||||||
|
break;
|
||||||
|
case TYPE_CODE_STANDARD:
|
||||||
|
this.type = StreamResolution.ResolutionType.STANDARD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid type code: " + resString.charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse options until we reach the resolution start delimiter
|
||||||
|
int currentIndex = 1;
|
||||||
|
while (resString.charAt(currentIndex) != RES_START_DELIMITER) {
|
||||||
|
switch (resString.charAt(currentIndex)) {
|
||||||
|
case OPTION_CODE_PORTRAIT:
|
||||||
|
portrait = true;
|
||||||
|
break;
|
||||||
|
case OPTION_CODE_FULL_SCREEN:
|
||||||
|
fullScreen = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid option code: " + resString.charAt(currentIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the type code before splitting
|
||||||
|
String[] dims = resString.substring(currentIndex).split(""+RES_DIM_DELIMITER);
|
||||||
|
this.width = Integer.parseInt(dims[0]);
|
||||||
|
this.height = Integer.parseInt(dims[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toPrefString() {
|
||||||
|
String str = "";
|
||||||
|
|
||||||
|
// Start with the type code
|
||||||
|
switch (type) {
|
||||||
|
case NATIVE:
|
||||||
|
str += TYPE_CODE_NATIVE;
|
||||||
|
break;
|
||||||
|
case STANDARD:
|
||||||
|
str += TYPE_CODE_STANDARD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid type: "+type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now any option codes that may apply
|
||||||
|
if (portrait) {
|
||||||
|
str += OPTION_CODE_PORTRAIT;
|
||||||
|
}
|
||||||
|
if (fullScreen) {
|
||||||
|
str += OPTION_CODE_FULL_SCREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally the dimensions
|
||||||
|
str += RES_START_DELIMITER;
|
||||||
|
str += width;
|
||||||
|
str += RES_DIM_DELIMITER;
|
||||||
|
str += height;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,19 +122,21 @@ public class StreamSettings extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class SettingsFragment extends PreferenceFragment {
|
public static class SettingsFragment extends PreferenceFragment {
|
||||||
private int nativeResolutionStartIndex = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
private void setValue(String preferenceKey, String value) {
|
private void setValue(String preferenceKey, String value) {
|
||||||
ListPreference pref = (ListPreference) findPreference(preferenceKey);
|
ListPreference pref = (ListPreference) findPreference(preferenceKey);
|
||||||
|
|
||||||
pref.setValue(value);
|
pref.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addNativeResolutionEntry(int nativeWidth, int nativeHeight, boolean insetsRemoved, boolean portrait) {
|
private void addResolutionEntry(int width, int height, PreferenceConfiguration.StreamResolution.ResolutionType type,
|
||||||
|
boolean insetsRemoved, boolean portrait) {
|
||||||
ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING);
|
ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING);
|
||||||
|
PreferenceConfiguration.StreamResolution newRes =
|
||||||
|
new PreferenceConfiguration.StreamResolution(width, height, type, portrait, insetsRemoved);
|
||||||
|
|
||||||
String newName;
|
String newName;
|
||||||
|
|
||||||
|
if (type == PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||||
if (insetsRemoved) {
|
if (insetsRemoved) {
|
||||||
newName = getResources().getString(R.string.resolution_prefix_native_fullscreen);
|
newName = getResources().getString(R.string.resolution_prefix_native_fullscreen);
|
||||||
}
|
}
|
||||||
@ -142,7 +144,7 @@ public class StreamSettings extends Activity {
|
|||||||
newName = getResources().getString(R.string.resolution_prefix_native);
|
newName = getResources().getString(R.string.resolution_prefix_native);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferenceConfiguration.isSquarishScreen(nativeWidth, nativeHeight)) {
|
if (PreferenceConfiguration.isSquarishScreen(width, height)) {
|
||||||
if (portrait) {
|
if (portrait) {
|
||||||
newName += " " + getResources().getString(R.string.resolution_prefix_native_portrait);
|
newName += " " + getResources().getString(R.string.resolution_prefix_native_portrait);
|
||||||
}
|
}
|
||||||
@ -150,16 +152,19 @@ public class StreamSettings extends Activity {
|
|||||||
newName += " " + getResources().getString(R.string.resolution_prefix_native_landscape);
|
newName += " " + getResources().getString(R.string.resolution_prefix_native_landscape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newName = null;
|
||||||
|
}
|
||||||
|
|
||||||
newName += " ("+nativeWidth+"x"+nativeHeight+")";
|
newName += " ("+width+"x"+height+")";
|
||||||
|
|
||||||
String newValue = nativeWidth+"x"+nativeHeight;
|
|
||||||
|
|
||||||
CharSequence[] values = pref.getEntryValues();
|
CharSequence[] values = pref.getEntryValues();
|
||||||
|
|
||||||
// Check if the native resolution is already present
|
// Check if the new resolution is already present
|
||||||
for (CharSequence value : values) {
|
for (CharSequence value : values) {
|
||||||
if (newValue.equals(value.toString())) {
|
PreferenceConfiguration.StreamResolution existingResolution = new PreferenceConfiguration.StreamResolution(value.toString());
|
||||||
|
if (existingResolution.width == newRes.width && existingResolution.height == newRes.height) {
|
||||||
// It is present in the default list, so don't add it again
|
// It is present in the default list, so don't add it again
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -168,23 +173,23 @@ public class StreamSettings extends Activity {
|
|||||||
CharSequence[] newEntries = Arrays.copyOf(pref.getEntries(), pref.getEntries().length + 1);
|
CharSequence[] newEntries = Arrays.copyOf(pref.getEntries(), pref.getEntries().length + 1);
|
||||||
CharSequence[] newValues = Arrays.copyOf(values, values.length + 1);
|
CharSequence[] newValues = Arrays.copyOf(values, values.length + 1);
|
||||||
|
|
||||||
// Add the new native option
|
// Add the new resolution option
|
||||||
newEntries[newEntries.length - 1] = newName;
|
newEntries[newEntries.length - 1] = newName;
|
||||||
newValues[newValues.length - 1] = newValue;
|
newValues[newValues.length - 1] = newRes.toPrefString();
|
||||||
|
|
||||||
pref.setEntries(newEntries);
|
pref.setEntries(newEntries);
|
||||||
pref.setEntryValues(newValues);
|
pref.setEntryValues(newValues);
|
||||||
|
|
||||||
if (newValues.length - 1 < nativeResolutionStartIndex) {
|
|
||||||
nativeResolutionStartIndex = newValues.length - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addNativeResolutionEntries(int nativeWidth, int nativeHeight, boolean insetsRemoved) {
|
private void addNativeResolutionEntries(int nativeWidth, int nativeHeight, boolean insetsRemoved) {
|
||||||
if (PreferenceConfiguration.isSquarishScreen(nativeWidth, nativeHeight)) {
|
if (PreferenceConfiguration.isSquarishScreen(nativeWidth, nativeHeight)) {
|
||||||
addNativeResolutionEntry(nativeHeight, nativeWidth, insetsRemoved, true);
|
addResolutionEntry(nativeHeight, nativeWidth,
|
||||||
|
PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE,
|
||||||
|
insetsRemoved, true);
|
||||||
}
|
}
|
||||||
addNativeResolutionEntry(nativeWidth, nativeHeight, insetsRemoved, false);
|
addResolutionEntry(nativeWidth, nativeHeight,
|
||||||
|
PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE,
|
||||||
|
insetsRemoved, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeValue(String preferenceKey, String value, Runnable onMatched) {
|
private void removeValue(String preferenceKey, String value, Runnable onMatched) {
|
||||||
@ -608,20 +613,10 @@ public class StreamSettings extends Activity {
|
|||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
||||||
String valueStr = (String) newValue;
|
String valueStr = (String) newValue;
|
||||||
|
PreferenceConfiguration.StreamResolution resolution = new PreferenceConfiguration.StreamResolution(valueStr);
|
||||||
// Detect if this value is the native resolution option
|
|
||||||
CharSequence[] values = ((ListPreference)preference).getEntryValues();
|
|
||||||
boolean isNativeRes = true;
|
|
||||||
for (int i = 0; i < values.length; i++) {
|
|
||||||
// Look for a match prior to the start of the native resolution entries
|
|
||||||
if (valueStr.equals(values[i].toString()) && i < nativeResolutionStartIndex) {
|
|
||||||
isNativeRes = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is native resolution, show the warning dialog
|
// If this is native resolution, show the warning dialog
|
||||||
if (isNativeRes) {
|
if (resolution.type == PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||||
Dialog.displayDialog(getActivity(),
|
Dialog.displayDialog(getActivity(),
|
||||||
getResources().getString(R.string.title_native_res_dialog),
|
getResources().getString(R.string.title_native_res_dialog),
|
||||||
getResources().getString(R.string.text_native_res_dialog),
|
getResources().getString(R.string.text_native_res_dialog),
|
||||||
|
@ -9,14 +9,13 @@
|
|||||||
<item>@string/resolution_4k</item>
|
<item>@string/resolution_4k</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<!-- Keep this in sync with PreferenceConfiguration.isNativeResolution()! -->
|
|
||||||
<string-array name="resolution_values" translatable="false">
|
<string-array name="resolution_values" translatable="false">
|
||||||
<item>640x360</item>
|
<item>s_640x360</item>
|
||||||
<item>854x480</item>
|
<item>s_854x480</item>
|
||||||
<item>1280x720</item>
|
<item>s_1280x720</item>
|
||||||
<item>1920x1080</item>
|
<item>s_1920x1080</item>
|
||||||
<item>2560x1440</item>
|
<item>s_2560x1440</item>
|
||||||
<item>3840x2160</item>
|
<item>s_3840x2160</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="fps_names">
|
<string-array name="fps_names">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user