From 6acb1fd92a3c6f039ac103c24357650a596f9cc5 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 22 Jan 2015 14:13:02 -0500 Subject: [PATCH] Initial support for GFE 2.2.2+ --- .../com/limelight/nvstream/NvConnection.java | 6 +-- .../nvstream/av/video/VideoStream.java | 41 ------------------ .../nvstream/control/ControlStream.java | 27 +++++------- .../nvstream/rtsp/RtspConnection.java | 4 +- .../limelight/nvstream/rtsp/SdpGenerator.java | 43 +++---------------- 5 files changed, 21 insertions(+), 100 deletions(-) diff --git a/moonlight-common/src/com/limelight/nvstream/NvConnection.java b/moonlight-common/src/com/limelight/nvstream/NvConnection.java index 351a62eb..15ebc5f0 100644 --- a/moonlight-common/src/com/limelight/nvstream/NvConnection.java +++ b/moonlight-common/src/com/limelight/nvstream/NvConnection.java @@ -103,9 +103,9 @@ public class NvConnection { NvHTTP h = new NvHTTP(hostAddr, uniqueId, localDeviceName, cryptoProvider); String serverInfo = h.getServerInfo(uniqueId); - - if (!h.getServerVersion(serverInfo).startsWith("3.")) { - listener.displayMessage("Limelight now requires GeForce Experience 2.1.1 or later. Please upgrade GFE on your PC and try again."); + String serverVersion = h.getServerVersion(serverInfo); + if (!serverVersion.startsWith("4.")) { + listener.displayMessage("Limelight now requires GeForce Experience 2.2.2 or later. Please upgrade GFE on your PC and try again."); return false; } diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java b/moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java index c0b073e7..0f8906a2 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java +++ b/moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java @@ -20,7 +20,6 @@ import com.limelight.nvstream.av.RtpReorderQueue; public class VideoStream { public static final int RTP_PORT = 47998; public static final int RTCP_PORT = 47999; - public static final int FIRST_FRAME_PORT = 47996; public static final int FIRST_FRAME_TIMEOUT = 5000; public static final int RTP_RECV_BUFFER = 256 * 1024; @@ -100,40 +99,6 @@ public class VideoStream { threads.clear(); } - private void connectFirstFrame() throws IOException - { - firstFrameSocket = new Socket(); - firstFrameSocket.setSoTimeout(FIRST_FRAME_TIMEOUT); - firstFrameSocket.connect(new InetSocketAddress(host, FIRST_FRAME_PORT), FIRST_FRAME_TIMEOUT); - } - - private void readFirstFrame() throws IOException - { - byte[] firstFrame = new byte[streamConfig.getMaxPacketSize()]; - - try { - InputStream firstFrameStream = firstFrameSocket.getInputStream(); - - int offset = 0; - for (;;) - { - int bytesRead = firstFrameStream.read(firstFrame, offset, firstFrame.length-offset); - - if (bytesRead == -1) - break; - - offset += bytesRead; - } - - // We can actually ignore this data. It's the act of reading it that matters. - // If this changes, we'll need to move this call before startReceiveThread() - // to avoid state corruption in the depacketizer - } finally { - firstFrameSocket.close(); - firstFrameSocket = null; - } - } - public void setupRtpSession() throws SocketException { rtp = new DatagramSocket(); @@ -184,16 +149,10 @@ public class VideoStream { startReceiveThread(); } - // Connect to the first frame port to open UDP 47998 - connectFirstFrame(); - // Start pinging before reading the first frame // so GFE knows where to send UDP data startUdpPingThread(); - // Read the first frame to start the flow of video - readFirstFrame(); - return true; } diff --git a/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java b/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java index 57ff6bd3..28003d09 100644 --- a/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java +++ b/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java @@ -20,21 +20,22 @@ public class ControlStream implements ConnectionStatusListener { public static final int CONTROL_TIMEOUT = 5000; - public static final short PTYPE_START_STREAM_A = 0x140b; - public static final short PPAYLEN_START_STREAM_A = 1; - public static final byte[] PPAYLOAD_START_STREAM_A = new byte[]{0}; + public static final short PTYPE_START_STREAM_A = 0x0606; + public static final short PPAYLEN_START_STREAM_A = 2; + public static final byte[] PPAYLOAD_START_STREAM_A = new byte[]{0, 0}; - public static final short PTYPE_START_STREAM_B = 0x1410; - public static final short PPAYLEN_START_STREAM_B = 16; + public static final short PTYPE_START_STREAM_B = 0x0609; + public static final short PPAYLEN_START_STREAM_B = 1; + public static final byte[] PPAYLOAD_START_STREAM_B = new byte[]{0}; - public static final short PTYPE_RESYNC = 0x1404; + public static final short PTYPE_RESYNC = 0x0604; public static final short PPAYLEN_RESYNC = 24; - public static final short PTYPE_LOSS_STATS = 0x140c; + public static final short PTYPE_LOSS_STATS = 0x060a; public static final short PPAYLEN_LOSS_STATS = 32; // Currently unused - public static final short PTYPE_FRAME_STATS = 0x1417; + public static final short PTYPE_FRAME_STATS = 0x0611; public static final short PPAYLEN_FRAME_STATS = 64; public static final int LOSS_REPORT_INTERVAL_MS = 50; @@ -233,14 +234,8 @@ public class ControlStream implements ConnectionStatusListener { private ControlStream.NvCtlResponse doStartB() throws IOException { - ByteBuffer payload = ByteBuffer.wrap(new byte[PPAYLEN_START_STREAM_B]).order(ByteOrder.LITTLE_ENDIAN); - - payload.putInt(0); - payload.putInt(0); - payload.putInt(0); - payload.putInt(0xa); - - return sendAndGetReply(new NvCtlPacket(PTYPE_START_STREAM_B, PPAYLEN_START_STREAM_B, payload.array())); + return sendAndGetReply(new NvCtlPacket(PTYPE_START_STREAM_B, + PPAYLEN_START_STREAM_B, PPAYLOAD_START_STREAM_B)); } private void sendResync(int firstLostFrame, int nextSuccessfulFrame) throws IOException diff --git a/moonlight-common/src/com/limelight/nvstream/rtsp/RtspConnection.java b/moonlight-common/src/com/limelight/nvstream/rtsp/RtspConnection.java index afbd8189..01ada0ab 100644 --- a/moonlight-common/src/com/limelight/nvstream/rtsp/RtspConnection.java +++ b/moonlight-common/src/com/limelight/nvstream/rtsp/RtspConnection.java @@ -17,8 +17,8 @@ public class RtspConnection { public static final int PORT = 48010; public static final int RTSP_TIMEOUT = 5000; - // GFE 2.1.1 - public static final int CLIENT_VERSION = 10; + // GFE 2.2.2+ + public static final int CLIENT_VERSION = 11; private int sequenceNumber = 1; private int sessionId = 0; diff --git a/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java b/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java index 961187c9..374e5a07 100644 --- a/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java +++ b/moonlight-common/src/com/limelight/nvstream/rtsp/SdpGenerator.java @@ -2,29 +2,11 @@ package com.limelight.nvstream.rtsp; import java.net.InetAddress; import java.net.Inet6Address; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import com.limelight.nvstream.StreamConfiguration; public class SdpGenerator { - private static void addSessionAttributeBytes(StringBuilder config, String attribute, byte[] value) { - char str[] = new char[value.length]; - - for (int i = 0; i < value.length; i++) { - str[i] = (char)value[i]; - } - - addSessionAttribute(config, attribute, new String(str)); - } - - private static void addSessionAttributeInt(StringBuilder config, String attribute, int value) { - ByteBuffer b = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN); - b.putInt(value); - addSessionAttributeBytes(config, attribute, b.array()); - } - private static void addSessionAttribute(StringBuilder config, String attribute, String value) { config.append("a="+attribute+":"+value+" \r\n"); } @@ -43,25 +25,15 @@ public class SdpGenerator { config.append("\r\n"); config.append("s=NVIDIA Streaming Client").append("\r\n"); - addSessionAttribute(config, "x-nv-general.serverAddress", host.getHostAddress()); - - addSessionAttributeInt(config, "x-nv-general.featureFlags", 0x42774141); + addSessionAttribute(config, "x-nv-general.serverAddress", "rtsp://"+host.getHostAddress()+":48010"); addSessionAttribute(config, "x-nv-video[0].clientViewportWd", ""+sc.getWidth()); addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+sc.getHeight()); addSessionAttribute(config, "x-nv-video[0].maxFPS", ""+sc.getRefreshRate()); addSessionAttribute(config, "x-nv-video[0].packetSize", ""+sc.getMaxPacketSize()); - - addSessionAttributeInt(config, "x-nv-video[0].transferProtocol", 0x41514141); - addSessionAttributeInt(config, "x-nv-video[1].transferProtocol", 0x41514141); - addSessionAttributeInt(config, "x-nv-video[2].transferProtocol", 0x41514141); - addSessionAttributeInt(config, "x-nv-video[3].transferProtocol", 0x41514141); - addSessionAttributeInt(config, "x-nv-video[0].rateControlMode", 0x42414141); - addSessionAttributeInt(config, "x-nv-video[1].rateControlMode", 0x42514141); - addSessionAttributeInt(config, "x-nv-video[2].rateControlMode", 0x42514141); - addSessionAttributeInt(config, "x-nv-video[3].rateControlMode", 0x42514141); + addSessionAttribute(config, "x-nv-video[0].rateControlMode", "4"); if (sc.getRemote()) { addSessionAttribute(config, "x-nv-video[0].averageBitrate", "4"); @@ -77,10 +49,10 @@ public class SdpGenerator { // Adding 100 causes the stream to start at a lower resolution /*if (sc.getAdaptiveResolutionEnabled()) { - addSessionAttribute(config, "x-nv-vqos[0].bw.flags", "16183"); + addSessionAttribute(config, "x-nv-vqos[0].bw.flags", "3895"); } else*/ { - addSessionAttribute(config, "x-nv-vqos[0].bw.flags", "14083"); + addSessionAttribute(config, "x-nv-vqos[0].bw.flags", "3895"); // Lock the bitrate if we're not scaling resolution so the picture doesn't get too bad if (sc.getHeight() >= 1080 && sc.getRefreshRate() >= 60) { @@ -126,11 +98,6 @@ public class SdpGenerator { addSessionAttribute(config, "x-nv-vqos[0].qosTrafficType", "5"); } - addSessionAttribute(config, "x-nv-vqos[0].videoQosMaxConsecutiveDrops", "0"); - addSessionAttribute(config, "x-nv-vqos[1].videoQosMaxConsecutiveDrops", "0"); - addSessionAttribute(config, "x-nv-vqos[2].videoQosMaxConsecutiveDrops", "0"); - addSessionAttribute(config, "x-nv-vqos[3].videoQosMaxConsecutiveDrops", "0"); - if (sc.getRemote()) { addSessionAttribute(config, "x-nv-aqos.qosTrafficType", "0"); } @@ -140,7 +107,7 @@ public class SdpGenerator { config.append("t=0 0").append("\r\n"); - config.append("m=video 47996 ").append("\r\n"); + config.append("m=video 47998 ").append("\r\n"); return config.toString(); }