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");
}
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);
} catch (NumberFormatException e) {
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.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.LinkedList;

View File

@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
@ -179,10 +178,20 @@ public class VideoStream {
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
// so GFE knows where to send UDP data
startUdpPingThread();
// Read the first frame on Gen 3 servers
if (context.serverGeneration == ConnectionContext.SERVER_GENERATION_3) {
readFirstFrame();
}
return true;
}

View File

@ -19,23 +19,55 @@ public class ControlStream implements ConnectionStatusListener {
public static final int CONTROL_TIMEOUT = 5000;
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};
private static final int IDX_START_A = 0;
private static final int IDX_START_B = 1;
private static final int IDX_RESYNC = 2;
private static final int IDX_LOSS_STATS = 3;
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};
private static final short packetTypesGen3[] = {
0x140b, // Start A
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;
public static final short PPAYLEN_RESYNC = 24;
private static final short payloadLengthsGen3[] = {
-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;
public static final short PPAYLEN_LOSS_STATS = 32;
// Currently unused
public static final short PTYPE_FRAME_STATS = 0x0611;
public static final short PPAYLEN_FRAME_STATS = 64;
private static final byte[] precontructedPayloadsGen3[] = {
new byte[]{0}, // Start A
null, // Start B
null, // Resync
null, // Loss Stats
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;
@ -62,9 +94,28 @@ public class ControlStream implements ConnectionStatusListener {
private LinkedBlockingQueue<int[]> invalidReferenceFrameTuples = new LinkedBlockingQueue<int[]>();
private boolean aborting = false;
private final short[] packetTypes;
private final short[] payloadLengths;
private final byte[][] preconstructedPayloads;
public ControlStream(ConnectionContext 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
@ -102,7 +153,8 @@ public class ControlStream implements ConnectionStatusListener {
bb.putInt(0);
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()
@ -148,7 +200,7 @@ public class ControlStream implements ConnectionStatusListener {
lossStatsThread = new Thread() {
@Override
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())
{
@ -225,19 +277,33 @@ public class ControlStream implements ConnectionStatusListener {
private ControlStream.NvCtlResponse doStartA() throws IOException
{
return sendAndGetReply(new NvCtlPacket(PTYPE_START_STREAM_A,
PPAYLEN_START_STREAM_A, PPAYLOAD_START_STREAM_A));
return sendAndGetReply(new NvCtlPacket(packetTypes[IDX_START_A],
(short) preconstructedPayloads[IDX_START_A].length,
preconstructedPayloads[IDX_START_A]));
}
private ControlStream.NvCtlResponse doStartB() throws IOException
{
return sendAndGetReply(new NvCtlPacket(PTYPE_START_STREAM_B,
PPAYLEN_START_STREAM_B, PPAYLOAD_START_STREAM_B));
if (context.serverGeneration == ConnectionContext.SERVER_GENERATION_3) {
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
{
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(nextSuccessfulFrame);
@ -245,7 +311,8 @@ public class ControlStream implements ConnectionStatusListener {
conf.putLong(0xFFFFF);
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 {