Add negotiation logic for 4K and H.265

This commit is contained in:
Cameron Gutman 2015-12-12 17:18:15 -08:00
parent 920154b4b6
commit 4c67631ea5
6 changed files with 100 additions and 6 deletions

View File

@ -24,4 +24,6 @@ public class ConnectionContext {
public int serverGeneration;
public VideoFormat negotiatedVideoFormat;
public int negotiatedWidth, negotiatedHeight;
public int negotiatedFps;
}

View File

@ -15,6 +15,7 @@ import com.limelight.LimeLog;
import com.limelight.nvstream.av.audio.AudioStream;
import com.limelight.nvstream.av.audio.AudioRenderer;
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
import com.limelight.nvstream.av.video.VideoDecoderRenderer.VideoFormat;
import com.limelight.nvstream.av.video.VideoStream;
import com.limelight.nvstream.control.ControlStream;
import com.limelight.nvstream.http.GfeHttpResponseException;
@ -61,6 +62,8 @@ public class NvConnection {
}
this.context.riKeyId = generateRiKeyId();
this.context.negotiatedVideoFormat = VideoFormat.Unknown;
}
private static SecretKey generateRiAesKey() throws NoSuchAlgorithmException {
@ -139,6 +142,41 @@ public class NvConnection {
return false;
}
//
// Decide on negotiated stream parameters now
//
// Check for a supported stream resolution
if (context.streamConfig.getHeight() >= 2160 && !h.supports4K(serverInfo)) {
// Client wants 4K but the server can't do it
context.connListener.displayTransientMessage("Your PC does not have a supported GPU or GFE version for 4K streaming. The stream will be 1080p.");
// Lower resolution to 1080p
context.negotiatedWidth = 1920;
context.negotiatedHeight = 1080;
}
else {
// Take what the client wanted
context.negotiatedWidth = context.streamConfig.getWidth();
context.negotiatedHeight = context.streamConfig.getHeight();
}
// For now, always take the client's FPS request
context.negotiatedFps = context.streamConfig.getRefreshRate();
// Determine whether we should request H.265 video
String gpuType = h.getGpuType(serverInfo);
if (context.streamConfig.getHevcSupported() && // Client wants it
h.getMaxLumaPixelsHEVC(serverInfo) > 0 && gpuType != null && // Check if GFE version supports it
gpuType.contains("GTX 9")) // Check if GPU can do it (only 900-series) - TODO: Find a better way to detect this
{
context.negotiatedVideoFormat = VideoFormat.H265;
}
else
{
context.negotiatedVideoFormat = VideoFormat.H264;
}
NvApp app = context.streamConfig.getApp();
// If the client did not provide an exact app ID, do a lookup with the applist
@ -407,4 +445,8 @@ public class NvConnection {
inputStream.sendMouseScroll(scrollClicks);
}
public VideoFormat getActiveVideoFormat() {
return context.negotiatedVideoFormat;
}
}

View File

@ -4,6 +4,7 @@ import com.limelight.nvstream.av.DecodeUnit;
public abstract class VideoDecoderRenderer {
public enum VideoFormat {
Unknown,
H264,
H265
};

View File

@ -122,8 +122,8 @@ public class VideoStream {
if (decRend != null) {
try {
if (!decRend.setup(context.negotiatedVideoFormat, context.streamConfig.getWidth(),
context.streamConfig.getHeight(), context.streamConfig.getRefreshRate(),
if (!decRend.setup(context.negotiatedVideoFormat, context.negotiatedWidth,
context.negotiatedHeight, context.negotiatedFps,
renderTarget, drFlags)) {
return false;
}

View File

@ -334,6 +334,55 @@ public class NvHTTP {
public PairingManager.PairState getPairState(String serverInfo) throws IOException, XmlPullParserException {
return pm.getPairState(serverInfo);
}
public long getMaxLumaPixelsH264(String serverInfo) throws XmlPullParserException, IOException {
String str = getXmlString(serverInfo, "MaxLumaPixelsH264");
if (str != null) {
try {
return Long.parseLong(str);
} catch (NumberFormatException e) {
return 0;
}
} else {
return 0;
}
}
public long getMaxLumaPixelsHEVC(String serverInfo) throws XmlPullParserException, IOException {
String str = getXmlString(serverInfo, "MaxLumaPixelsHEVC");
if (str != null) {
try {
return Long.parseLong(str);
} catch (NumberFormatException e) {
return 0;
}
} else {
return 0;
}
}
public String getGpuType(String serverInfo) throws XmlPullParserException, IOException {
return getXmlString(serverInfo, "gputype");
}
public boolean supports4K(String serverInfo) throws XmlPullParserException, IOException {
// serverinfo returns supported resolutions in descending order, so getting the first
// height will give us whether we support 4K. If this is not present, we don't support
// 4K.
String heightStr = getXmlString(serverInfo, "Height");
if (heightStr == null) {
return false;
}
try {
if (Integer.parseInt(heightStr) >= 2160) {
// Found a 4K resolution in the list
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
@ -481,7 +530,7 @@ public class NvHTTP {
String xmlStr = openHttpConnectionToString(baseUrlHttps +
"/launch?uniqueid=" + uniqueId +
"&appid=" + appId +
"&mode=" + context.streamConfig.getWidth() + "x" + context.streamConfig.getHeight() + "x" + context.streamConfig.getRefreshRate() +
"&mode=" + context.negotiatedWidth + "x" + context.negotiatedHeight + "x" + context.negotiatedFps +
"&additionalStates=1&sops=" + (context.streamConfig.getSops() ? 1 : 0) +
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
"&rikeyid="+context.riKeyId +

View File

@ -95,9 +95,9 @@ public class SdpGenerator {
config.append("\r\n");
config.append("s=NVIDIA Streaming Client").append("\r\n");
addSessionAttribute(config, "x-nv-video[0].clientViewportWd", ""+context.streamConfig.getWidth());
addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+context.streamConfig.getHeight());
addSessionAttribute(config, "x-nv-video[0].maxFPS", ""+context.streamConfig.getRefreshRate());
addSessionAttribute(config, "x-nv-video[0].clientViewportWd", ""+context.negotiatedWidth);
addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+context.negotiatedHeight);
addSessionAttribute(config, "x-nv-video[0].maxFPS", ""+context.negotiatedFps);
addSessionAttribute(config, "x-nv-video[0].packetSize", ""+context.streamConfig.getMaxPacketSize());