mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-18 18:42:46 +00:00
Add support for 90/120 FPS streaming and 1440p
This commit is contained in:
parent
be620908f9
commit
6adc9dcb2d
@ -7,7 +7,11 @@ import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class PreferenceConfiguration {
|
||||
static final String RES_FPS_PREF_STRING = "list_resolution_fps";
|
||||
private static final String LEGACY_RES_FPS_PREF_STRING = "list_resolution_fps";
|
||||
|
||||
|
||||
static final String RESOLUTION_PREF_STRING = "list_resolution";
|
||||
static final String FPS_PREF_STRING = "list_fps";
|
||||
static final String BITRATE_PREF_STRING = "seekbar_bitrate_kbps";
|
||||
private static final String BITRATE_PREF_OLD_STRING = "seekbar_bitrate";
|
||||
private static final String STRETCH_PREF_STRING = "checkbox_stretch_video";
|
||||
@ -30,17 +34,8 @@ public class PreferenceConfiguration {
|
||||
private static final String BIND_ALL_USB_STRING = "checkbox_usb_bind_all";
|
||||
private static final String MOUSE_EMULATION_STRING = "checkbox_mouse_emulation";
|
||||
|
||||
private static final int BITRATE_DEFAULT_360_30 = 1000;
|
||||
private static final int BITRATE_DEFAULT_360_60 = 2000;
|
||||
private static final int BITRATE_DEFAULT_720_30 = 5000;
|
||||
private static final int BITRATE_DEFAULT_720_60 = 10000;
|
||||
private static final int BITRATE_DEFAULT_1080_30 = 10000;
|
||||
private static final int BITRATE_DEFAULT_1080_60 = 20000;
|
||||
private static final int BITRATE_DEFAULT_4K_30 = 40000;
|
||||
private static final int BITRATE_DEFAULT_4K_60 = 80000;
|
||||
|
||||
private static final String DEFAULT_RES_FPS = "720p60";
|
||||
private static final int DEFAULT_BITRATE = BITRATE_DEFAULT_720_60;
|
||||
static final String DEFAULT_RESOLUTION = "720p";
|
||||
static final String DEFAULT_FPS = "60";
|
||||
private static final boolean DEFAULT_STRETCH = false;
|
||||
private static final boolean DEFAULT_SOPS = true;
|
||||
private static final boolean DEFAULT_DISABLE_TOASTS = false;
|
||||
@ -54,7 +49,6 @@ public class PreferenceConfiguration {
|
||||
private static final String DEFAULT_VIDEO_FORMAT = "auto";
|
||||
private static final boolean ONSCREEN_CONTROLLER_DEFAULT = false;
|
||||
private static final boolean ONLY_L3_R3_DEFAULT = false;
|
||||
private static final boolean DEFAULT_BATTERY_SAVER = false;
|
||||
private static final boolean DEFAULT_DISABLE_FRAME_DROP = false;
|
||||
private static final boolean DEFAULT_ENABLE_HDR = false;
|
||||
private static final boolean DEFAULT_ENABLE_PIP = false;
|
||||
@ -80,34 +74,77 @@ public class PreferenceConfiguration {
|
||||
public boolean bindAllUsb;
|
||||
public boolean mouseEmulation;
|
||||
|
||||
public static int getDefaultBitrate(String resFpsString) {
|
||||
if (resFpsString.equals("360p30")) {
|
||||
return BITRATE_DEFAULT_360_30;
|
||||
private static int getHeightFromResolutionString(String resString) {
|
||||
if (resString.equalsIgnoreCase("360p")) {
|
||||
return 360;
|
||||
}
|
||||
else if (resFpsString.equals("360p60")) {
|
||||
return BITRATE_DEFAULT_360_60;
|
||||
else if (resString.equalsIgnoreCase("720p")) {
|
||||
return 720;
|
||||
}
|
||||
else if (resFpsString.equals("720p30")) {
|
||||
return BITRATE_DEFAULT_720_30;
|
||||
else if (resString.equalsIgnoreCase("1080p")) {
|
||||
return 1080;
|
||||
}
|
||||
else if (resFpsString.equals("720p60")) {
|
||||
return BITRATE_DEFAULT_720_60;
|
||||
else if (resString.equalsIgnoreCase("1440p")) {
|
||||
return 1440;
|
||||
}
|
||||
else if (resFpsString.equals("1080p30")) {
|
||||
return BITRATE_DEFAULT_1080_30;
|
||||
}
|
||||
else if (resFpsString.equals("1080p60")) {
|
||||
return BITRATE_DEFAULT_1080_60;
|
||||
}
|
||||
else if (resFpsString.equals("4K30")) {
|
||||
return BITRATE_DEFAULT_4K_30;
|
||||
}
|
||||
else if (resFpsString.equals("4K60")) {
|
||||
return BITRATE_DEFAULT_4K_60;
|
||||
else if (resString.equalsIgnoreCase("4K")) {
|
||||
return 3840;
|
||||
}
|
||||
else {
|
||||
// Should never get here
|
||||
return DEFAULT_BITRATE;
|
||||
// Should be unreachable
|
||||
return 720;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getWidthFromResolutionString(String resString) {
|
||||
return (getHeightFromResolutionString(resString) * 16) / 9;
|
||||
}
|
||||
|
||||
private static String getResolutionString(int width, int height) {
|
||||
switch (height) {
|
||||
case 360:
|
||||
return "360p";
|
||||
default:
|
||||
case 720:
|
||||
return "720p";
|
||||
case 1080:
|
||||
return "1080p";
|
||||
case 1440:
|
||||
return "1440p";
|
||||
case 3840:
|
||||
return "4K";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDefaultBitrate(String resString, String fpsString) {
|
||||
int width = getWidthFromResolutionString(resString);
|
||||
int height = getHeightFromResolutionString(resString);
|
||||
int fps = Integer.parseInt(fpsString);
|
||||
|
||||
// This table prefers 16:10 resolutions because they are
|
||||
// only slightly more pixels than the 16:9 equivalents, so
|
||||
// we don't want to bump those 16:10 resolutions up to the
|
||||
// next 16:9 slot.
|
||||
//
|
||||
// This logic is shamelessly stolen from Moonlight Qt:
|
||||
// https://github.com/moonlight-stream/moonlight-qt/blob/master/app/settings/streamingpreferences.cpp
|
||||
|
||||
if (width * height <= 640 * 360) {
|
||||
return (int)(1000 * (fps / 30.0));
|
||||
}
|
||||
// This covers 1280x720 and 1280x800 too
|
||||
else if (width * height <= 1366 * 768) {
|
||||
return (int)(5000 * (fps / 30.0));
|
||||
}
|
||||
else if (width * height <= 1920 * 1200) {
|
||||
return (int)(10000 * (fps / 30.0));
|
||||
}
|
||||
else if (width * height <= 2560 * 1600) {
|
||||
return (int)(20000 * (fps / 30.0));
|
||||
}
|
||||
else /* if (width * height <= 3840 * 2160) */ {
|
||||
return (int)(40000 * (fps / 30.0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +170,9 @@ public class PreferenceConfiguration {
|
||||
|
||||
public static int getDefaultBitrate(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return getDefaultBitrate(prefs.getString(RES_FPS_PREF_STRING, DEFAULT_RES_FPS));
|
||||
return getDefaultBitrate(
|
||||
prefs.getString(RESOLUTION_PREF_STRING, DEFAULT_RESOLUTION),
|
||||
prefs.getString(FPS_PREF_STRING, DEFAULT_FPS));
|
||||
}
|
||||
|
||||
private static int getVideoFormatValue(Context context) {
|
||||
@ -161,7 +200,9 @@ public class PreferenceConfiguration {
|
||||
prefs.edit()
|
||||
.remove(BITRATE_PREF_STRING)
|
||||
.remove(BITRATE_PREF_OLD_STRING)
|
||||
.remove(RES_FPS_PREF_STRING)
|
||||
.remove(LEGACY_RES_FPS_PREF_STRING)
|
||||
.remove(RESOLUTION_PREF_STRING)
|
||||
.remove(FPS_PREF_STRING)
|
||||
.remove(VIDEO_FORMAT_PREF_STRING)
|
||||
.remove(ENABLE_HDR_PREF_STRING)
|
||||
.apply();
|
||||
@ -171,12 +212,9 @@ public class PreferenceConfiguration {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
PreferenceConfiguration config = new PreferenceConfiguration();
|
||||
|
||||
config.bitrate = prefs.getInt(BITRATE_PREF_STRING, prefs.getInt(BITRATE_PREF_OLD_STRING, 0) * 1000);
|
||||
if (config.bitrate == 0) {
|
||||
config.bitrate = getDefaultBitrate(context);
|
||||
}
|
||||
|
||||
String str = prefs.getString(RES_FPS_PREF_STRING, DEFAULT_RES_FPS);
|
||||
// Migrate legacy preferences to the new locations
|
||||
String str = prefs.getString(LEGACY_RES_FPS_PREF_STRING, null);
|
||||
if (str != null) {
|
||||
if (str.equals("360p30")) {
|
||||
config.width = 640;
|
||||
config.height = 360;
|
||||
@ -224,6 +262,26 @@ public class PreferenceConfiguration {
|
||||
config.fps = 60;
|
||||
}
|
||||
|
||||
prefs.edit()
|
||||
.remove(LEGACY_RES_FPS_PREF_STRING)
|
||||
.putString(RESOLUTION_PREF_STRING, getResolutionString(config.width, config.height))
|
||||
.putString(FPS_PREF_STRING, ""+config.fps)
|
||||
.apply();
|
||||
}
|
||||
else {
|
||||
// Use the new preference location
|
||||
String resStr = prefs.getString(RESOLUTION_PREF_STRING, PreferenceConfiguration.DEFAULT_RESOLUTION);
|
||||
config.width = PreferenceConfiguration.getWidthFromResolutionString(resStr);
|
||||
config.height = PreferenceConfiguration.getHeightFromResolutionString(resStr);
|
||||
config.fps = Integer.parseInt(prefs.getString(FPS_PREF_STRING, PreferenceConfiguration.DEFAULT_FPS));
|
||||
}
|
||||
|
||||
// This must happen after the preferences migration to ensure the preferences are populated
|
||||
config.bitrate = prefs.getInt(BITRATE_PREF_STRING, prefs.getInt(BITRATE_PREF_OLD_STRING, 0) * 1000);
|
||||
if (config.bitrate == 0) {
|
||||
config.bitrate = getDefaultBitrate(context);
|
||||
}
|
||||
|
||||
config.videoFormat = getVideoFormatValue(context);
|
||||
|
||||
config.deadzonePercentage = prefs.getInt(DEADZONE_PREF_STRING, DEFAULT_DEADZONE);
|
||||
|
@ -58,12 +58,12 @@ public class StreamSettings extends Activity {
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragment {
|
||||
|
||||
private static void removeResolution(ListPreference pref, String prefix) {
|
||||
private static void removeValue(ListPreference pref, String value) {
|
||||
int matchingCount = 0;
|
||||
|
||||
// Count the number of matching entries we'll be removing
|
||||
for (CharSequence seq : pref.getEntryValues()) {
|
||||
if (seq.toString().startsWith(prefix)) {
|
||||
if (seq.toString().equalsIgnoreCase(value)) {
|
||||
matchingCount++;
|
||||
}
|
||||
}
|
||||
@ -73,8 +73,8 @@ public class StreamSettings extends Activity {
|
||||
CharSequence[] entryValues = new CharSequence[pref.getEntryValues().length-matchingCount];
|
||||
int outIndex = 0;
|
||||
for (int i = 0; i < pref.getEntryValues().length; i++) {
|
||||
if (pref.getEntryValues()[i].toString().startsWith(prefix)) {
|
||||
// Skip matching prefixes
|
||||
if (pref.getEntryValues()[i].toString().equalsIgnoreCase(value)) {
|
||||
// Skip matching values
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -110,6 +110,8 @@ public class StreamSettings extends Activity {
|
||||
category.removePreference(findPreference("checkbox_enable_pip"));
|
||||
}
|
||||
|
||||
int maxSupportedFps = 0;
|
||||
|
||||
// Hide non-supported resolution/FPS combinations
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
||||
@ -134,9 +136,16 @@ public class StreamSettings extends Activity {
|
||||
if ((width >= 3840 || height >= 2160) && maxSupportedResW < 3840) {
|
||||
maxSupportedResW = 3840;
|
||||
}
|
||||
else if ((width >= 2560 || height >= 1440) && maxSupportedResW < 2560) {
|
||||
maxSupportedResW = 2560;
|
||||
}
|
||||
else if ((width >= 1920 || height >= 1080) && maxSupportedResW < 1920) {
|
||||
maxSupportedResW = 1920;
|
||||
}
|
||||
|
||||
if (candidate.getRefreshRate() > maxSupportedFps) {
|
||||
maxSupportedFps = (int)candidate.getRefreshRate();
|
||||
}
|
||||
}
|
||||
|
||||
// This must be called to do runtime initialization before calling functions that evaluate
|
||||
@ -186,20 +195,35 @@ public class StreamSettings extends Activity {
|
||||
|
||||
LimeLog.info("Maximum resolution slot: "+maxSupportedResW);
|
||||
|
||||
ListPreference resPref = (ListPreference) findPreference("list_resolution_fps");
|
||||
ListPreference resPref = (ListPreference) findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING);
|
||||
if (maxSupportedResW != 0) {
|
||||
if (maxSupportedResW < 3840) {
|
||||
// 4K is unsupported
|
||||
removeResolution(resPref, "4K");
|
||||
removeValue(resPref, "4K");
|
||||
}
|
||||
if (maxSupportedResW < 2560) {
|
||||
// 1440p is unsupported
|
||||
removeValue(resPref, "1440p");
|
||||
}
|
||||
if (maxSupportedResW < 1920) {
|
||||
// 1080p is unsupported
|
||||
removeResolution(resPref, "1080p");
|
||||
removeValue(resPref, "1080p");
|
||||
}
|
||||
// Never remove 720p
|
||||
}
|
||||
}
|
||||
|
||||
ListPreference fpsPref = (ListPreference) findPreference(PreferenceConfiguration.FPS_PREF_STRING);
|
||||
// We give some extra room in case the FPS is rounded down
|
||||
if (maxSupportedFps < 118) {
|
||||
removeValue(fpsPref, "120");
|
||||
}
|
||||
if (maxSupportedFps < 88) {
|
||||
// 1080p is unsupported
|
||||
removeValue(fpsPref, "90");
|
||||
}
|
||||
// Never remove 30 FPS or 60 FPS
|
||||
|
||||
// Remove HDR preference for devices below Nougat
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
LimeLog.info("Excluding HDR toggle based on OS");
|
||||
@ -232,8 +256,7 @@ public class StreamSettings extends Activity {
|
||||
|
||||
// Add a listener to the FPS and resolution preference
|
||||
// so the bitrate can be auto-adjusted
|
||||
Preference pref = findPreference(PreferenceConfiguration.RES_FPS_PREF_STRING);
|
||||
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
||||
@ -242,7 +265,27 @@ public class StreamSettings extends Activity {
|
||||
// Write the new bitrate value
|
||||
prefs.edit()
|
||||
.putInt(PreferenceConfiguration.BITRATE_PREF_STRING,
|
||||
PreferenceConfiguration.getDefaultBitrate(valueStr))
|
||||
PreferenceConfiguration.getDefaultBitrate(valueStr,
|
||||
prefs.getString(PreferenceConfiguration.FPS_PREF_STRING,
|
||||
PreferenceConfiguration.DEFAULT_FPS)))
|
||||
.apply();
|
||||
|
||||
// Allow the original preference change to take place
|
||||
return true;
|
||||
}
|
||||
});
|
||||
findPreference(PreferenceConfiguration.FPS_PREF_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
||||
String valueStr = (String) newValue;
|
||||
|
||||
// Write the new bitrate value
|
||||
prefs.edit()
|
||||
.putInt(PreferenceConfiguration.BITRATE_PREF_STRING,
|
||||
PreferenceConfiguration.getDefaultBitrate(
|
||||
prefs.getString(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.DEFAULT_RESOLUTION),
|
||||
valueStr))
|
||||
.apply();
|
||||
|
||||
// Allow the original preference change to take place
|
||||
|
@ -1,24 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="resolution_names">
|
||||
<item>360p 30 FPS</item>
|
||||
<item>360p 60 FPS</item>
|
||||
<item>720p 30 FPS</item>
|
||||
<item>720p 60 FPS</item>
|
||||
<item>1080p 30 FPS</item>
|
||||
<item>1080p 60 FPS</item>
|
||||
<item>4K 30 FPS</item>
|
||||
<item>4K 60 FPS</item>
|
||||
<item>360p</item>
|
||||
<item>720p</item>
|
||||
<item>1080p</item>
|
||||
<item>1440p</item>
|
||||
<item>4K</item>
|
||||
</string-array>
|
||||
<string-array name="resolution_values" translatable="false">
|
||||
<item>360p30</item>
|
||||
<item>360p60</item>
|
||||
<item>720p30</item>
|
||||
<item>720p60</item>
|
||||
<item>1080p30</item>
|
||||
<item>1080p60</item>
|
||||
<item>4K30</item>
|
||||
<item>4K60</item>
|
||||
<item>360p</item>
|
||||
<item>720p</item>
|
||||
<item>1080p</item>
|
||||
<item>1440p</item>
|
||||
<item>4K</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="fps_names">
|
||||
<item>30 FPS</item>
|
||||
<item>60 FPS</item>
|
||||
<item>90 FPS</item>
|
||||
<item>120 FPS</item>
|
||||
</string-array>
|
||||
<string-array name="fps_values" translatable="false">
|
||||
<item>30</item>
|
||||
<item>60</item>
|
||||
<item>90</item>
|
||||
<item>120</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="language_names" translatable="false">
|
||||
|
@ -114,10 +114,12 @@
|
||||
|
||||
<!-- Preferences -->
|
||||
<string name="category_basic_settings">Basic Settings</string>
|
||||
<string name="title_resolution_list">Select resolution and FPS target</string>
|
||||
<string name="summary_resolution_list">Setting values too high for your device may cause lag or crashing</string>
|
||||
<string name="title_seekbar_bitrate">Select target video bitrate</string>
|
||||
<string name="summary_seekbar_bitrate">Lower bitrate to reduce stuttering. Raise bitrate to increase image quality.</string>
|
||||
<string name="title_resolution_list">Video resolution</string>
|
||||
<string name="summary_resolution_list">Increase to improve image clarity. Decrease for better performance on lower end devices and slower networks.</string>
|
||||
<string name="title_fps_list">Video frame rate</string>
|
||||
<string name="summary_fps_list">Increase for a smoother video stream. Decrease for better performance on lower end devices.</string>
|
||||
<string name="title_seekbar_bitrate">Video bitrate</string>
|
||||
<string name="summary_seekbar_bitrate">Increase for better image quality. Decrease to improve performance on slower connections.</string>
|
||||
<string name="suffix_seekbar_bitrate">Kbps</string>
|
||||
<string name="title_checkbox_stretch_video">Stretch video to full-screen</string>
|
||||
<string name="title_checkbox_disable_warnings">Disable warning messages</string>
|
||||
|
@ -5,12 +5,19 @@
|
||||
<PreferenceCategory android:title="@string/category_basic_settings"
|
||||
android:key="category_basic_settings">
|
||||
<ListPreference
|
||||
android:key="list_resolution_fps"
|
||||
android:key="list_resolution"
|
||||
android:title="@string/title_resolution_list"
|
||||
android:summary="@string/summary_resolution_list"
|
||||
android:entries="@array/resolution_names"
|
||||
android:entryValues="@array/resolution_values"
|
||||
android:defaultValue="720p60" />
|
||||
android:defaultValue="720p" />
|
||||
<ListPreference
|
||||
android:key="list_fps"
|
||||
android:title="@string/title_fps_list"
|
||||
android:summary="@string/summary_fps_list"
|
||||
android:entries="@array/fps_names"
|
||||
android:entryValues="@array/fps_values"
|
||||
android:defaultValue="60" />
|
||||
<com.limelight.preferences.SeekBarPreference
|
||||
android:key="seekbar_bitrate_kbps"
|
||||
android:dialogMessage="@string/summary_seekbar_bitrate"
|
||||
|
Loading…
x
Reference in New Issue
Block a user