mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-21 03:52:48 +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 int serverGeneration;
|
||||||
|
|
||||||
public VideoFormat negotiatedVideoFormat;
|
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.AudioStream;
|
||||||
import com.limelight.nvstream.av.audio.AudioRenderer;
|
import com.limelight.nvstream.av.audio.AudioRenderer;
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
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.av.video.VideoStream;
|
||||||
import com.limelight.nvstream.control.ControlStream;
|
import com.limelight.nvstream.control.ControlStream;
|
||||||
import com.limelight.nvstream.http.GfeHttpResponseException;
|
import com.limelight.nvstream.http.GfeHttpResponseException;
|
||||||
@ -61,6 +62,8 @@ public class NvConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.context.riKeyId = generateRiKeyId();
|
this.context.riKeyId = generateRiKeyId();
|
||||||
|
|
||||||
|
this.context.negotiatedVideoFormat = VideoFormat.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SecretKey generateRiAesKey() throws NoSuchAlgorithmException {
|
private static SecretKey generateRiAesKey() throws NoSuchAlgorithmException {
|
||||||
@ -139,6 +142,41 @@ public class NvConnection {
|
|||||||
return false;
|
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();
|
NvApp app = context.streamConfig.getApp();
|
||||||
|
|
||||||
// If the client did not provide an exact app ID, do a lookup with the applist
|
// 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);
|
inputStream.sendMouseScroll(scrollClicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VideoFormat getActiveVideoFormat() {
|
||||||
|
return context.negotiatedVideoFormat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import com.limelight.nvstream.av.DecodeUnit;
|
|||||||
|
|
||||||
public abstract class VideoDecoderRenderer {
|
public abstract class VideoDecoderRenderer {
|
||||||
public enum VideoFormat {
|
public enum VideoFormat {
|
||||||
|
Unknown,
|
||||||
H264,
|
H264,
|
||||||
H265
|
H265
|
||||||
};
|
};
|
||||||
|
@ -122,8 +122,8 @@ public class VideoStream {
|
|||||||
|
|
||||||
if (decRend != null) {
|
if (decRend != null) {
|
||||||
try {
|
try {
|
||||||
if (!decRend.setup(context.negotiatedVideoFormat, context.streamConfig.getWidth(),
|
if (!decRend.setup(context.negotiatedVideoFormat, context.negotiatedWidth,
|
||||||
context.streamConfig.getHeight(), context.streamConfig.getRefreshRate(),
|
context.negotiatedHeight, context.negotiatedFps,
|
||||||
renderTarget, drFlags)) {
|
renderTarget, drFlags)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -334,6 +334,55 @@ public class NvHTTP {
|
|||||||
public PairingManager.PairState getPairState(String serverInfo) throws IOException, XmlPullParserException {
|
public PairingManager.PairState getPairState(String serverInfo) throws IOException, XmlPullParserException {
|
||||||
return pm.getPairState(serverInfo);
|
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 {
|
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
|
// 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 +
|
String xmlStr = openHttpConnectionToString(baseUrlHttps +
|
||||||
"/launch?uniqueid=" + uniqueId +
|
"/launch?uniqueid=" + uniqueId +
|
||||||
"&appid=" + appId +
|
"&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) +
|
"&additionalStates=1&sops=" + (context.streamConfig.getSops() ? 1 : 0) +
|
||||||
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
||||||
"&rikeyid="+context.riKeyId +
|
"&rikeyid="+context.riKeyId +
|
||||||
|
@ -95,9 +95,9 @@ public class SdpGenerator {
|
|||||||
config.append("\r\n");
|
config.append("\r\n");
|
||||||
config.append("s=NVIDIA Streaming Client").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].clientViewportWd", ""+context.negotiatedWidth);
|
||||||
addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+context.streamConfig.getHeight());
|
addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+context.negotiatedHeight);
|
||||||
addSessionAttribute(config, "x-nv-video[0].maxFPS", ""+context.streamConfig.getRefreshRate());
|
addSessionAttribute(config, "x-nv-video[0].maxFPS", ""+context.negotiatedFps);
|
||||||
|
|
||||||
addSessionAttribute(config, "x-nv-video[0].packetSize", ""+context.streamConfig.getMaxPacketSize());
|
addSessionAttribute(config, "x-nv-video[0].packetSize", ""+context.streamConfig.getMaxPacketSize());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user