Backwards compatibility for video and control stream to GFE 2.1.x

This commit is contained in:
Cameron Gutman 2015-01-25 17:59:23 -05:00
parent daf7598774
commit aee255a6ee
4 changed files with 109 additions and 24 deletions

View File

@ -119,6 +119,16 @@ public class NvConnection {
context.connListener.displayTransientMessage("This version of GFE is not currently supported. You may experience issues until Limelight is updated"); context.connListener.displayTransientMessage("This version of GFE is not currently supported. You may experience issues until Limelight is updated");
} }
switch (majorVersion) {
case 3:
context.serverGeneration = ConnectionContext.SERVER_GENERATION_3;
break;
case 4:
default:
context.serverGeneration = ConnectionContext.SERVER_GENERATION_4;
break;
}
LimeLog.info("Server major version: "+majorVersion); LimeLog.info("Server major version: "+majorVersion);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
context.connListener.displayMessage("Server version malformed: "+serverVersion); context.connListener.displayMessage("Server version malformed: "+serverVersion);

View File

@ -3,7 +3,6 @@ package com.limelight.nvstream.av.audio;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketException; import java.net.SocketException;
import java.util.LinkedList; import java.util.LinkedList;

View File

@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
@ -179,10 +178,20 @@ public class VideoStream {
startReceiveThread(); startReceiveThread();
} }
// Open the first frame port connection on Gen 3 servers
if (context.serverGeneration == ConnectionContext.SERVER_GENERATION_3) {
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 on Gen 3 servers
if (context.serverGeneration == ConnectionContext.SERVER_GENERATION_3) {
readFirstFrame();
}
return true; return true;
} }

View File

@ -19,23 +19,55 @@ 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 = 0x0606; private static final int IDX_START_A = 0;
public static final short PPAYLEN_START_STREAM_A = 2; private static final int IDX_START_B = 1;
public static final byte[] PPAYLOAD_START_STREAM_A = new byte[]{0, 0}; private static final int IDX_RESYNC = 2;
private static final int IDX_LOSS_STATS = 3;
public static final short PTYPE_START_STREAM_B = 0x0609; private static final short packetTypesGen3[] = {
public static final short PPAYLEN_START_STREAM_B = 1; 0x140b, // Start A
public static final byte[] PPAYLOAD_START_STREAM_B = new byte[]{0}; 0x1410, // Start B
0x1404, // Resync
0x140c, // Loss Stats
0x1417, // Frame Stats (unused)
};
private static final short packetTypesGen4[] = {
0x0606, // Start A
0x0609, // Start B
0x0604, // Resync
0x060a, // Loss Stats
0x0611, // Frame Stats (unused)
};
public static final short PTYPE_RESYNC = 0x0604; private static final short payloadLengthsGen3[] = {
public static final short PPAYLEN_RESYNC = 24; -1, // Start A
16, // Start B
24, // Resync
32, // Loss Stats
64, // Frame Stats
};
private static final short payloadLengthsGen4[] = {
-1, // Start A
-1, // Start B
24, // Resync
32, // Loss Stats
64, // Frame Stats
};
public static final short PTYPE_LOSS_STATS = 0x060a; private static final byte[] precontructedPayloadsGen3[] = {
public static final short PPAYLEN_LOSS_STATS = 32; new byte[]{0}, // Start A
null, // Start B
// Currently unused null, // Resync
public static final short PTYPE_FRAME_STATS = 0x0611; null, // Loss Stats
public static final short PPAYLEN_FRAME_STATS = 64; null, // Frame Stats
};
private static final byte[] precontructedPayloadsGen4[] = {
new byte[]{0, 0}, // Start A
new byte[]{0}, // Start B
null, // Resync
null, // Loss Stats
null, // Frame Stats
};
public static final int LOSS_REPORT_INTERVAL_MS = 50; public static final int LOSS_REPORT_INTERVAL_MS = 50;
@ -62,9 +94,28 @@ public class ControlStream implements ConnectionStatusListener {
private LinkedBlockingQueue<int[]> invalidReferenceFrameTuples = new LinkedBlockingQueue<int[]>(); private LinkedBlockingQueue<int[]> invalidReferenceFrameTuples = new LinkedBlockingQueue<int[]>();
private boolean aborting = false; private boolean aborting = false;
private final short[] packetTypes;
private final short[] payloadLengths;
private final byte[][] preconstructedPayloads;
public ControlStream(ConnectionContext context) public ControlStream(ConnectionContext context)
{ {
this.context = context; this.context = context;
switch (context.serverGeneration)
{
case ConnectionContext.SERVER_GENERATION_3:
packetTypes = packetTypesGen3;
payloadLengths = payloadLengthsGen3;
preconstructedPayloads = precontructedPayloadsGen3;
break;
case ConnectionContext.SERVER_GENERATION_4:
default:
packetTypes = packetTypesGen4;
payloadLengths = payloadLengthsGen4;
preconstructedPayloads = precontructedPayloadsGen4;
break;
}
} }
public void initialize() throws IOException public void initialize() throws IOException
@ -102,7 +153,8 @@ public class ControlStream implements ConnectionStatusListener {
bb.putInt(0); bb.putInt(0);
bb.putInt(0x14); bb.putInt(0x14);
sendPacket(new NvCtlPacket(PTYPE_LOSS_STATS, PPAYLEN_LOSS_STATS, bb.array())); sendPacket(new NvCtlPacket(packetTypes[IDX_LOSS_STATS],
payloadLengths[IDX_LOSS_STATS], bb.array()));
} }
public void abort() public void abort()
@ -148,7 +200,7 @@ public class ControlStream implements ConnectionStatusListener {
lossStatsThread = new Thread() { lossStatsThread = new Thread() {
@Override @Override
public void run() { public void run() {
ByteBuffer bb = ByteBuffer.allocate(PPAYLEN_LOSS_STATS).order(ByteOrder.LITTLE_ENDIAN); ByteBuffer bb = ByteBuffer.allocate(payloadLengths[IDX_LOSS_STATS]).order(ByteOrder.LITTLE_ENDIAN);
while (!isInterrupted()) while (!isInterrupted())
{ {
@ -225,19 +277,33 @@ public class ControlStream implements ConnectionStatusListener {
private ControlStream.NvCtlResponse doStartA() throws IOException private ControlStream.NvCtlResponse doStartA() throws IOException
{ {
return sendAndGetReply(new NvCtlPacket(PTYPE_START_STREAM_A, return sendAndGetReply(new NvCtlPacket(packetTypes[IDX_START_A],
PPAYLEN_START_STREAM_A, PPAYLOAD_START_STREAM_A)); (short) preconstructedPayloads[IDX_START_A].length,
preconstructedPayloads[IDX_START_A]));
} }
private ControlStream.NvCtlResponse doStartB() throws IOException private ControlStream.NvCtlResponse doStartB() throws IOException
{ {
return sendAndGetReply(new NvCtlPacket(PTYPE_START_STREAM_B, if (context.serverGeneration == ConnectionContext.SERVER_GENERATION_3) {
PPAYLEN_START_STREAM_B, PPAYLOAD_START_STREAM_B)); ByteBuffer payload = ByteBuffer.wrap(new byte[payloadLengths[IDX_START_B]]).order(ByteOrder.LITTLE_ENDIAN);
payload.putInt(0);
payload.putInt(0);
payload.putInt(0);
payload.putInt(0xa);
return sendAndGetReply(new NvCtlPacket(packetTypes[IDX_START_B],
payloadLengths[IDX_START_B], payload.array()));
}
else {
return sendAndGetReply(new NvCtlPacket(packetTypes[IDX_START_B],
payloadLengths[IDX_START_B], preconstructedPayloads[IDX_START_B]));
}
} }
private void sendResync(int firstLostFrame, int nextSuccessfulFrame) throws IOException private void sendResync(int firstLostFrame, int nextSuccessfulFrame) throws IOException
{ {
ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLEN_RESYNC]).order(ByteOrder.LITTLE_ENDIAN); ByteBuffer conf = ByteBuffer.wrap(new byte[payloadLengths[IDX_RESYNC]]).order(ByteOrder.LITTLE_ENDIAN);
//conf.putLong(firstLostFrame); //conf.putLong(firstLostFrame);
//conf.putLong(nextSuccessfulFrame); //conf.putLong(nextSuccessfulFrame);
@ -245,7 +311,8 @@ public class ControlStream implements ConnectionStatusListener {
conf.putLong(0xFFFFF); conf.putLong(0xFFFFF);
conf.putLong(0); conf.putLong(0);
sendAndGetReply(new NvCtlPacket(PTYPE_RESYNC, PPAYLEN_RESYNC, conf.array())); sendAndGetReply(new NvCtlPacket(packetTypes[IDX_RESYNC],
payloadLengths[IDX_RESYNC], conf.array()));
} }
static class NvCtlPacket { static class NvCtlPacket {