mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-21 12:03:02 +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);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user