Initial support for GFE 2.2.2+

This commit is contained in:
Cameron Gutman 2015-01-22 14:13:02 -05:00
parent bd2a1b8886
commit 6acb1fd92a
5 changed files with 21 additions and 100 deletions

View File

@ -103,9 +103,9 @@ public class NvConnection {
NvHTTP h = new NvHTTP(hostAddr, uniqueId, localDeviceName, cryptoProvider); NvHTTP h = new NvHTTP(hostAddr, uniqueId, localDeviceName, cryptoProvider);
String serverInfo = h.getServerInfo(uniqueId); String serverInfo = h.getServerInfo(uniqueId);
String serverVersion = h.getServerVersion(serverInfo);
if (!h.getServerVersion(serverInfo).startsWith("3.")) { if (!serverVersion.startsWith("4.")) {
listener.displayMessage("Limelight now requires GeForce Experience 2.1.1 or later. Please upgrade GFE on your PC and try again."); listener.displayMessage("Limelight now requires GeForce Experience 2.2.2 or later. Please upgrade GFE on your PC and try again.");
return false; return false;
} }

View File

@ -20,7 +20,6 @@ import com.limelight.nvstream.av.RtpReorderQueue;
public class VideoStream { public class VideoStream {
public static final int RTP_PORT = 47998; public static final int RTP_PORT = 47998;
public static final int RTCP_PORT = 47999; 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 FIRST_FRAME_TIMEOUT = 5000;
public static final int RTP_RECV_BUFFER = 256 * 1024; public static final int RTP_RECV_BUFFER = 256 * 1024;
@ -100,40 +99,6 @@ public class VideoStream {
threads.clear(); 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 public void setupRtpSession() throws SocketException
{ {
rtp = new DatagramSocket(); rtp = new DatagramSocket();
@ -184,16 +149,10 @@ public class VideoStream {
startReceiveThread(); startReceiveThread();
} }
// Connect to the first frame port to open UDP 47998
connectFirstFrame();
// Start pinging before reading the first frame // Start pinging before reading the first frame
// so GFE knows where to send UDP data // so GFE knows where to send UDP data
startUdpPingThread(); startUdpPingThread();
// Read the first frame to start the flow of video
readFirstFrame();
return true; return true;
} }

View File

@ -20,21 +20,22 @@ public class ControlStream implements ConnectionStatusListener {
public static final int CONTROL_TIMEOUT = 5000; public static final int CONTROL_TIMEOUT = 5000;
public static final short PTYPE_START_STREAM_A = 0x140b; public static final short PTYPE_START_STREAM_A = 0x0606;
public static final short PPAYLEN_START_STREAM_A = 1; public static final short PPAYLEN_START_STREAM_A = 2;
public static final byte[] PPAYLOAD_START_STREAM_A = new byte[]{0}; 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 PTYPE_START_STREAM_B = 0x0609;
public static final short PPAYLEN_START_STREAM_B = 16; 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 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; public static final short PPAYLEN_LOSS_STATS = 32;
// Currently unused // 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 short PPAYLEN_FRAME_STATS = 64;
public static final int LOSS_REPORT_INTERVAL_MS = 50; public static final int LOSS_REPORT_INTERVAL_MS = 50;
@ -233,14 +234,8 @@ public class ControlStream implements ConnectionStatusListener {
private ControlStream.NvCtlResponse doStartB() throws IOException private ControlStream.NvCtlResponse doStartB() throws IOException
{ {
ByteBuffer payload = ByteBuffer.wrap(new byte[PPAYLEN_START_STREAM_B]).order(ByteOrder.LITTLE_ENDIAN); return sendAndGetReply(new NvCtlPacket(PTYPE_START_STREAM_B,
PPAYLEN_START_STREAM_B, PPAYLOAD_START_STREAM_B));
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()));
} }
private void sendResync(int firstLostFrame, int nextSuccessfulFrame) throws IOException private void sendResync(int firstLostFrame, int nextSuccessfulFrame) throws IOException

View File

@ -17,8 +17,8 @@ public class RtspConnection {
public static final int PORT = 48010; public static final int PORT = 48010;
public static final int RTSP_TIMEOUT = 5000; public static final int RTSP_TIMEOUT = 5000;
// GFE 2.1.1 // GFE 2.2.2+
public static final int CLIENT_VERSION = 10; public static final int CLIENT_VERSION = 11;
private int sequenceNumber = 1; private int sequenceNumber = 1;
private int sessionId = 0; private int sessionId = 0;

View File

@ -2,29 +2,11 @@ package com.limelight.nvstream.rtsp;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import com.limelight.nvstream.StreamConfiguration; import com.limelight.nvstream.StreamConfiguration;
public class SdpGenerator { 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) { private static void addSessionAttribute(StringBuilder config, String attribute, String value) {
config.append("a="+attribute+":"+value+" \r\n"); config.append("a="+attribute+":"+value+" \r\n");
} }
@ -43,9 +25,7 @@ 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-general.serverAddress", host.getHostAddress()); addSessionAttribute(config, "x-nv-general.serverAddress", "rtsp://"+host.getHostAddress()+":48010");
addSessionAttributeInt(config, "x-nv-general.featureFlags", 0x42774141);
addSessionAttribute(config, "x-nv-video[0].clientViewportWd", ""+sc.getWidth()); addSessionAttribute(config, "x-nv-video[0].clientViewportWd", ""+sc.getWidth());
addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+sc.getHeight()); addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+sc.getHeight());
@ -53,15 +33,7 @@ public class SdpGenerator {
addSessionAttribute(config, "x-nv-video[0].packetSize", ""+sc.getMaxPacketSize()); addSessionAttribute(config, "x-nv-video[0].packetSize", ""+sc.getMaxPacketSize());
addSessionAttributeInt(config, "x-nv-video[0].transferProtocol", 0x41514141); addSessionAttribute(config, "x-nv-video[0].rateControlMode", "4");
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);
if (sc.getRemote()) { if (sc.getRemote()) {
addSessionAttribute(config, "x-nv-video[0].averageBitrate", "4"); 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 // Adding 100 causes the stream to start at a lower resolution
/*if (sc.getAdaptiveResolutionEnabled()) { /*if (sc.getAdaptiveResolutionEnabled()) {
addSessionAttribute(config, "x-nv-vqos[0].bw.flags", "16183"); addSessionAttribute(config, "x-nv-vqos[0].bw.flags", "3895");
} }
else*/ { 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 // Lock the bitrate if we're not scaling resolution so the picture doesn't get too bad
if (sc.getHeight() >= 1080 && sc.getRefreshRate() >= 60) { 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].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()) { if (sc.getRemote()) {
addSessionAttribute(config, "x-nv-aqos.qosTrafficType", "0"); addSessionAttribute(config, "x-nv-aqos.qosTrafficType", "0");
} }
@ -140,7 +107,7 @@ public class SdpGenerator {
config.append("t=0 0").append("\r\n"); 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(); return config.toString();
} }