mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-21 03:52:48 +00:00
Initial support for GFE 2.2.2+
This commit is contained in:
parent
bd2a1b8886
commit
6acb1fd92a
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user