mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-19 19:13:03 +00:00
Add negotiation logic for 4K and H.265
This commit is contained in:
parent
920154b4b6
commit
4c67631ea5
@ -24,4 +24,6 @@ public class ConnectionContext {
|
||||
public int serverGeneration;
|
||||
|
||||
public VideoFormat negotiatedVideoFormat;
|
||||
public int negotiatedWidth, negotiatedHeight;
|
||||
public int negotiatedFps;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.limelight.nvstream.av.DecodeUnit;
|
||||
|
||||
public abstract class VideoDecoderRenderer {
|
||||
public enum VideoFormat {
|
||||
Unknown,
|
||||
H264,
|
||||
H265
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 +
|
||||
|
@ -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());
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user