From b392d7f8e308e255ac6fa8440a82d9bbd458fd74 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 23 Dec 2020 16:17:06 -0600 Subject: [PATCH] Add option to stream at device native resolution Fixes #155 --- .../video/MediaCodecDecoderRenderer.java | 11 +++- .../com/limelight/nvstream/NvConnection.java | 12 ++++- .../preferences/PreferenceConfiguration.java | 1 + .../limelight/preferences/StreamSettings.java | 52 +++++++++++++++++++ app/src/main/res/values/arrays.xml | 2 + app/src/main/res/values/strings.xml | 2 + 6 files changed, 77 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java index 4d68ab22..e655774c 100644 --- a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java +++ b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -110,8 +110,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer { if (!MediaCodecHelper.decoderIsWhitelistedForHevc(decoderInfo.getName(), meteredNetwork)) { LimeLog.info("Found HEVC decoder, but it's not whitelisted - "+decoderInfo.getName()); - // HDR implies HEVC forced on, since HEVCMain10HDR10 is required for HDR - if (prefs.videoFormat == PreferenceConfiguration.FORCE_H265_ON || requestedHdr) { + // HDR implies HEVC forced on, since HEVCMain10HDR10 is required for HDR. + // > 4K streaming also requires HEVC, so force it on there too. + if (prefs.videoFormat == PreferenceConfiguration.FORCE_H265_ON || requestedHdr || + prefs.width > 4096 || prefs.height > 4096) { LimeLog.info("Forcing H265 enabled despite non-whitelisted decoder"); } else { @@ -250,6 +252,11 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer { return -1; } + if (width > 4096 || height > 4096) { + LimeLog.severe("> 4K streaming only supported on HEVC"); + return -1; + } + // These fixups only apply to H264 decoders needsSpsBitstreamFixup = MediaCodecHelper.decoderNeedsSpsBitstreamRestrictions(selectedDecoderName); needsBaselineSpsHack = MediaCodecHelper.decoderNeedsBaselineSpsHack(selectedDecoderName); diff --git a/app/src/main/java/com/limelight/nvstream/NvConnection.java b/app/src/main/java/com/limelight/nvstream/NvConnection.java index ac03a777..cf5c829d 100644 --- a/app/src/main/java/com/limelight/nvstream/NvConnection.java +++ b/app/src/main/java/com/limelight/nvstream/NvConnection.java @@ -115,7 +115,17 @@ public class NvConnection { // // Check for a supported stream resolution - if (context.streamConfig.getHeight() >= 2160 && !h.supports4K(serverInfo)) { + if ((context.streamConfig.getWidth() > 4096 || context.streamConfig.getHeight() > 4096) && + (h.getServerCodecModeSupport(serverInfo) & 0x200) == 0) { + context.connListener.displayMessage("Your host PC does not support streaming at resolutions above 4K."); + return false; + } + else if ((context.streamConfig.getWidth() > 4096 || context.streamConfig.getHeight() > 4096) && + !context.streamConfig.getHevcSupported()) { + context.connListener.displayMessage("Your streaming device must support H.265 to stream at resolutions above 4K."); + return false; + } + else if (context.streamConfig.getHeight() >= 2160 && !h.supports4K(serverInfo)) { // Client wants 4K but the server can't do it context.connListener.displayTransientMessage("You must update GeForce Experience to stream in 4K. The stream will be 1080p."); diff --git a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java index 8fd819a8..98dff8f6 100644 --- a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java +++ b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java @@ -83,6 +83,7 @@ public class PreferenceConfiguration { public static final String RES_1080P = "1920x1080"; public static final String RES_1440P = "2560x1440"; public static final String RES_4K = "3840x2160"; + public static final String RES_NATIVE = "Native"; public int width, height, fps; public int bitrate; diff --git a/app/src/main/java/com/limelight/preferences/StreamSettings.java b/app/src/main/java/com/limelight/preferences/StreamSettings.java index f45c1d47..91febd3b 100644 --- a/app/src/main/java/com/limelight/preferences/StreamSettings.java +++ b/app/src/main/java/com/limelight/preferences/StreamSettings.java @@ -25,6 +25,7 @@ import com.limelight.LimeLog; import com.limelight.PcView; import com.limelight.R; import com.limelight.binding.video.MediaCodecHelper; +import com.limelight.utils.Dialog; import com.limelight.utils.UiHelper; public class StreamSettings extends Activity { @@ -72,6 +73,23 @@ public class StreamSettings extends Activity { pref.setValue(value); } + private void updateNativeResolutionEntry(int nativeWidth, int nativeHeight) { + ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING); + + String nameSuffix = " ("+nativeWidth+"x"+nativeHeight+")"; + String newValue = nativeWidth+"x"+nativeHeight; + + CharSequence[] entries = pref.getEntries(); + CharSequence[] values = pref.getEntryValues(); + + // Add the name suffix to the native option + entries[entries.length - 1] = entries[entries.length - 1].toString() + nameSuffix; + values[values.length - 1] = newValue; + + pref.setEntries(entries); + pref.setEntryValues(values); + } + private void removeValue(String preferenceKey, String value, Runnable onMatched) { int matchingCount = 0; @@ -192,6 +210,7 @@ public class StreamSettings extends Activity { // HEVC Decoder: OMX.amlogic.hevc.decoder.awesome // AVC supported width range: 64 - 384 // HEVC supported width range: 64 - 544 + int nativeWidth = 0, nativeHeight = 0; for (Display.Mode candidate : display.getSupportedModes()) { // Some devices report their dimensions in the portrait orientation // where height > width. Normalize these to the conventional width > height @@ -200,6 +219,13 @@ public class StreamSettings extends Activity { int width = Math.max(candidate.getPhysicalWidth(), candidate.getPhysicalHeight()); int height = Math.min(candidate.getPhysicalWidth(), candidate.getPhysicalHeight()); + if (width > nativeWidth) { + nativeWidth = width; + } + if (height > nativeHeight) { + nativeHeight = height; + } + if ((width >= 3840 || height >= 2160) && maxSupportedResW < 3840) { maxSupportedResW = 3840; } @@ -215,6 +241,8 @@ public class StreamSettings extends Activity { } } + updateNativeResolutionEntry(nativeWidth, nativeHeight); + // This must be called to do runtime initialization before calling functions that evaluate // decoder lists. MediaCodecHelper.initialize(getContext(), GlPreferences.readPreferences(getContext()).glRenderer); @@ -299,6 +327,11 @@ public class StreamSettings extends Activity { // Never remove 720p } } + else { + updateNativeResolutionEntry( + getActivity().getWindowManager().getDefaultDisplay().getWidth(), + getActivity().getWindowManager().getDefaultDisplay().getHeight()); + } if (!PreferenceConfiguration.readPreferences(this.getActivity()).unlockFps) { // We give some extra room in case the FPS is rounded down @@ -408,6 +441,25 @@ public class StreamSettings extends Activity { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity()); String valueStr = (String) newValue; + // 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++) { + // If get a match prior to the end, it's not native res + if (valueStr.equals(values[i].toString()) && i < values.length - 1) { + isNativeRes = false; + break; + } + } + + // If this is native resolution, show the warning dialog + if (isNativeRes) { + Dialog.displayDialog(getActivity(), + getResources().getString(R.string.title_native_res_dialog), + getResources().getString(R.string.text_native_res_dialog), + false); + } + // Write the new bitrate value resetBitrateToDefault(prefs, valueStr, null); diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 2d0a4697..28a8ef4a 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -7,6 +7,7 @@ 1080p 1440p 4K + Native 640x360 @@ -15,6 +16,7 @@ 1920x1080 2560x1440 3840x2160 + Native diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 77dc03f4..99455617 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -138,6 +138,8 @@ Basic Settings Video resolution Increase to improve image clarity. Decrease for better performance on lower end devices and slower networks. + Native Resolution Limitations + Native resolution modes are not officially supported by GeForce Experience, so it will not set your host display resolution. You will need to set it manually while in game.\n\nIf your host PC display does not support this device\'s native resolution, you may need to add the resolution option manually in the NVIDIA Control Panel.\n\nFinally, your device or host PC may not support streaming at native resolution. If it doesn\'t work on your device, you\'re just out of luck. Video frame rate Increase for a smoother video stream. Decrease for better performance on lower end devices. Video bitrate