From a14a4a8d6070fc7cb4952026900729cf1c25b055 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 5 Oct 2016 18:45:19 -0700 Subject: [PATCH] Add support for GFE 3.0.7 --- .../limelight/nvstream/ConnectionContext.java | 3 ++ .../com/limelight/nvstream/NvConnection.java | 21 ++++++-- .../nvstream/av/video/VideoDepacketizer.java | 8 ++- .../com/limelight/nvstream/http/NvHTTP.java | 53 +++++++++++++++++-- 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/moonlight-common/src/com/limelight/nvstream/ConnectionContext.java b/moonlight-common/src/com/limelight/nvstream/ConnectionContext.java index 3baf08f2..b38717e2 100644 --- a/moonlight-common/src/com/limelight/nvstream/ConnectionContext.java +++ b/moonlight-common/src/com/limelight/nvstream/ConnectionContext.java @@ -32,6 +32,9 @@ public class ConnectionContext { public int serverGeneration; + // This is the version quad from the appversion tag of /serverinfo + public int[] serverAppVersion; + public VideoFormat negotiatedVideoFormat; public int negotiatedWidth, negotiatedHeight; public int negotiatedFps; diff --git a/moonlight-common/src/com/limelight/nvstream/NvConnection.java b/moonlight-common/src/com/limelight/nvstream/NvConnection.java index 0d740434..32130af2 100644 --- a/moonlight-common/src/com/limelight/nvstream/NvConnection.java +++ b/moonlight-common/src/com/limelight/nvstream/NvConnection.java @@ -105,7 +105,13 @@ public class NvConnection { String serverInfo = h.getServerInfo(); - int majorVersion = h.getServerMajorVersion(serverInfo); + context.serverAppVersion = h.getServerAppVersionQuad(serverInfo); + if (context.serverAppVersion == null) { + context.connListener.displayMessage("Server version malformed"); + return false; + } + + int majorVersion = context.serverAppVersion[0]; LimeLog.info("Server major version: "+majorVersion); if (majorVersion == 0) { @@ -158,16 +164,23 @@ public class NvConnection { // Lower resolution to 1080p context.negotiatedWidth = 1920; context.negotiatedHeight = 1080; + context.negotiatedFps = context.streamConfig.getRefreshRate(); + } + else if (context.streamConfig.getHeight() >= 2160 && context.streamConfig.getRefreshRate() >= 60 && !h.supports4K60(serverInfo)) { + // Client wants 4K 60 FPS but the server can't do it + context.connListener.displayTransientMessage("Your GPU does not support 4K 60 FPS streaming. The stream will be 4K 30 FPS."); + + context.negotiatedWidth = context.streamConfig.getWidth(); + context.negotiatedHeight = context.streamConfig.getHeight(); + context.negotiatedFps = 30; } else { // Take what the client wanted context.negotiatedWidth = context.streamConfig.getWidth(); context.negotiatedHeight = context.streamConfig.getHeight(); + context.negotiatedFps = context.streamConfig.getRefreshRate(); } - // For now, always take the client's FPS request - context.negotiatedFps = context.streamConfig.getRefreshRate(); - // // Video stream format will be decided during the RTSP handshake // diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java index dce813b7..3edac90c 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java +++ b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java @@ -50,7 +50,13 @@ public class VideoDepacketizer { this.controlListener = controlListener; this.nominalPacketDataLength = nominalPacketSize - VideoPacket.HEADER_SIZE; - if (context.serverGeneration >= ConnectionContext.SERVER_GENERATION_5) { + if ((context.serverAppVersion[0] > 7) || + (context.serverAppVersion[0] == 7 && context.serverAppVersion[1] > 1) || + (context.serverAppVersion[0] == 7 && context.serverAppVersion[1] == 1 && context.serverAppVersion[2] >= 320)) { + // Anything over 7.1.320 should use the 12 byte frame header + frameHeaderOffset = 12; + } + else if (context.serverGeneration >= ConnectionContext.SERVER_GENERATION_5) { // Gen 5 servers have an 8 byte header in the data portion of the first // packet of each frame frameHeaderOffset = 8; diff --git a/moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java b/moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java index d2a3402a..166fb0af 100644 --- a/moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java +++ b/moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java @@ -402,6 +402,30 @@ public class NvHTTP { return false; } + + public boolean supports4K60(String serverInfo) throws XmlPullParserException, IOException { + // If we don't support 4K at all, bail early + if (!supports4K(serverInfo)) { + return false; + } + + // serverinfo returns supported resolutions in descending order, so getting the first + // refresh rate will give us whether we support 4K60. If this is 30, we don't support + // 4K 60 FPS. + String fpsStr = getXmlString(serverInfo, "RefreshRate"); + if (fpsStr == null) { + return false; + } + + try { + if (Integer.parseInt(fpsStr) >= 60) { + // 4K supported and 60 FPS is the first entry + return true; + } + } catch (NumberFormatException ignored) {} + + return false; + } public int getCurrentGame(String serverInfo) throws IOException, XmlPullParserException { // GFE 2.8 started keeping currentgame set to the last game played. As a result, it no longer @@ -548,16 +572,35 @@ public class NvHTTP { } public int getServerMajorVersion(String serverInfo) throws XmlPullParserException, IOException { + int[] appVersionQuad = getServerAppVersionQuad(serverInfo); + if (appVersionQuad != null) { + return appVersionQuad[0]; + } + else { + return 0; + } + } + + public int[] getServerAppVersionQuad(String serverInfo) throws XmlPullParserException, IOException { try { String serverVersion = getServerVersion(serverInfo); - if (serverVersion == null || serverVersion.indexOf('.') < 0) { - LimeLog.warning("Malformed server version field"); - return 0; + if (serverVersion == null) { + LimeLog.warning("Missing server version field"); + return null; } - return Integer.parseInt(serverVersion.substring(0, serverVersion.indexOf('.'))); + String[] serverVersionSplit = serverVersion.split("\\."); + if (serverVersionSplit.length != 4) { + LimeLog.warning("Malformed server version field"); + return null; + } + int[] ret = new int[serverVersionSplit.length]; + for (int i = 0; i < ret.length; i++) { + ret[i] = Integer.parseInt(serverVersionSplit[i]); + } + return ret; } catch (NumberFormatException e) { e.printStackTrace(); - return 0; + return null; } }