Update control connection for GFE 2.0.1+. Remove config and handshake code that isn't used on GFE 2.0.1+

This commit is contained in:
Cameron Gutman 2014-06-28 21:19:39 -07:00
parent 929487249d
commit ee96cccb51
10 changed files with 147 additions and 358 deletions

View File

@ -8,6 +8,15 @@ void BbInitializeWrappedBuffer(PBYTE_BUFFER buff, char* data, int offset, int le
buff->byteOrder = byteOrder;
}
static long long byteSwapLongLong(PBYTE_BUFFER buff, long long l) {
if (buff->byteOrder == BYTE_ORDER_BIG) {
return HTONLL(l);
}
else {
return l;
}
}
static int byteSwapInt(PBYTE_BUFFER buff, int i) {
if (buff->byteOrder == BYTE_ORDER_BIG) {
return htonl(i);
@ -63,6 +72,19 @@ int BbGetInt(PBYTE_BUFFER buff, int *i) {
return 1;
}
int BbGetLong(PBYTE_BUFFER buff, long long *l) {
if (buff->position + sizeof(*l) > buff->length) {
return 0;
}
memcpy(l, &buff->buffer[buff->position], sizeof(*l));
buff->position += sizeof(*l);
*l = byteSwapInt(buff, *l);
return 1;
}
int BbPutInt(PBYTE_BUFFER buff, int i) {
if (buff->position + sizeof(i) > buff->length) {
return 0;
@ -76,6 +98,19 @@ int BbPutInt(PBYTE_BUFFER buff, int i) {
return 1;
}
int BbPutLong(PBYTE_BUFFER buff, long long l) {
if (buff->position + sizeof(l) > buff->length) {
return 0;
}
l = byteSwapInt(buff, l);
memcpy(&buff->buffer[buff->position], &l, sizeof(l));
buff->position += sizeof(l);
return 1;
}
int BbPutShort(PBYTE_BUFFER buff, short s) {
if (buff->position + sizeof(s) > buff->length) {
return 0;

View File

@ -5,6 +5,16 @@
#define BYTE_ORDER_LITTLE 1
#define BYTE_ORDER_BIG 2
#define HTONLL(x) \
((((x) & 0xff00000000000000ull) >> 56) \
| (((x) & 0x00ff000000000000ull) >> 40) \
| (((x) & 0x0000ff0000000000ull) >> 24) \
| (((x) & 0x000000ff00000000ull) >> 8) \
| (((x) & 0x00000000ff000000ull) << 8) \
| (((x) & 0x0000000000ff0000ull) << 24) \
| (((x) & 0x000000000000ff00ull) << 40) \
| (((x) & 0x00000000000000ffull) << 56))
typedef struct _BYTE_BUFFER {
char* buffer;
unsigned int offset;
@ -18,7 +28,9 @@ void BbInitializeWrappedBuffer(PBYTE_BUFFER buff, char* data, int offset, int le
int BbGet(PBYTE_BUFFER buff, char *c);
int BbGetShort(PBYTE_BUFFER buff, short *s);
int BbGetInt(PBYTE_BUFFER buff, int *i);
int BbGetLong(PBYTE_BUFFER buff, long long *l);
int BbPutInt(PBYTE_BUFFER buff, int i);
int BbPutShort(PBYTE_BUFFER buff, short s);
int BbPut(PBYTE_BUFFER buff, char c);
int BbPut(PBYTE_BUFFER buff, char c);
int BbPutLong(PBYTE_BUFFER buff, long long l);

View File

@ -1,135 +0,0 @@
#include "Limelight-internal.h"
#include "ByteBuffer.h"
static const int UNKNOWN_CONFIG[] = {
70151,
68291329,
1280,
68291584,
1280,
68291840,
15360,
68292096,
25600,
68292352,
2048,
68292608,
1024,
68289024,
262144,
17957632,
302055424,
134217729,
16777490,
70153,
68293120,
768000,
17961216,
303235072,
335609857,
838861842,
352321536,
1006634002,
369098752,
335545362,
385875968,
1042,
402653184,
134218770,
419430400,
167773202,
436207616,
855638290,
266779,
10000,
266780,
2000,
266781,
50,
266782,
3000,
266783,
2,
266794,
5000,
266795,
500,
266784,
75,
266785,
25,
266786,
10,
266787,
60,
266788,
30,
266789,
3,
266790,
1000,
266791,
5000,
266792,
5000,
266793,
5000,
70190,
68301063,
10240,
68301312,
6400,
68301568,
768000,
68299776,
768,
68300032,
2560,
68300544,
0,
34746368,
(int) 0xFE000000
};
static const int CONFIG_SIZE = sizeof(UNKNOWN_CONFIG) +(8 * 4) + 3;
int getConfigDataSize(PSTREAM_CONFIGURATION streamConfig) {
return CONFIG_SIZE;
}
char* allocateConfigDataForStreamConfig(PSTREAM_CONFIGURATION streamConfig) {
BYTE_BUFFER bb;
int i;
char* config = (char *)malloc(CONFIG_SIZE);
if (config == NULL) {
return NULL;
}
BbInitializeWrappedBuffer(&bb, config, 0, CONFIG_SIZE, BYTE_ORDER_LITTLE);
BbPutShort(&bb, 0x1204);
BbPutShort(&bb, 0x0004);
BbPutInt(&bb, streamConfig->width);
BbPutShort(&bb, 0x1205);
BbPutShort(&bb, 0x0004);
BbPutInt(&bb, streamConfig->height);
BbPutShort(&bb, 0x1206);
BbPutShort(&bb, 0x0004);
BbPutInt(&bb, 1);
BbPutShort(&bb, 0x120A);
BbPutShort(&bb, 0x0004);
BbPutInt(&bb, streamConfig->fps);
for (i = 0; i < sizeof(UNKNOWN_CONFIG) / sizeof(int); i++) {
BbPutInt(&bb, UNKNOWN_CONFIG[i]);
}
BbPutShort(&bb, 0x0013);
BbPut(&bb, 0x00);
return config;
}

View File

@ -2,6 +2,8 @@
#include "PlatformSockets.h"
#include "PlatformThreads.h"
#include "ByteBuffer.h"
typedef struct _NVCTL_PACKET_HEADER {
unsigned short type;
unsigned short payloadLength;
@ -10,26 +12,31 @@ typedef struct _NVCTL_PACKET_HEADER {
static IP_ADDRESS host;
static SOCKET ctlSock = INVALID_SOCKET;
static STREAM_CONFIGURATION streamConfig;
static PLT_THREAD heartbeatThread;
static PLT_THREAD jitterThread;
static PLT_THREAD lossStatsThread;
static PLT_THREAD resyncThread;
static PLT_EVENT resyncEvent;
static PCONNECTION_LISTENER_CALLBACKS listenerCallbacks;
static int lossCountSinceLastReport = 0;
static long currentFrame = 0;
static const short PTYPE_KEEPALIVE = 0x13ff;
static const short PPAYLEN_KEEPALIVE = 0x0000;
#define PTYPE_START_STREAM_A 0x140b
#define PPAYLEN_START_STREAM_A 1
static const char PPAYLOAD_START_STREAM_A[1] = { 0 };
static const short PTYPE_HEARTBEAT = 0x1401;
static const short PPAYLEN_HEARTBEAT = 0x0000;
#define PTYPE_START_STREAM_B 0x1410
#define PPAYLEN_START_STREAM_B 16
static const int PPAYLOAD_START_STREAM_B[4] = { 0, 0, 0, 0xa }; // FIXME: Little endian
static const short PTYPE_1405 = 0x1405;
static const short PPAYLEN_1405 = 0x0000;
#define PTYPE_RESYNC 0x1404
#define PPAYLEN_RESYNC 24
static const short PTYPE_RESYNC = 0x1404;
static const short PPAYLEN_RESYNC = 16;
#define PTYPE_LOSS_STATS 0x140c
#define PPAYLEN_LOSS_STATS 20
static const short PTYPE_JITTER = 0x140c;
static const short PPAYLEN_JITTER = 0x10;
#define PTYPE_FRAME_STATS 0x1417
#define PPAYLEN_FRAME_STATS 64
#define LOSS_REPORT_INTERVAL_MS 50
int initializeControlStream(IP_ADDRESS addr, PSTREAM_CONFIGURATION streamConfigPtr, PCONNECTION_LISTENER_CALLBACKS clCallbacks) {
memcpy(&streamConfig, streamConfigPtr, sizeof(*streamConfigPtr));
@ -56,6 +63,14 @@ void connectionDetectedFrameLoss(int startFrame, int endFrame) {
PltSetEvent(&resyncEvent);
}
void connectionReceivedFrame(int frameIndex) {
currentFrame = frameIndex;
}
void connectionLostPackets(int lastReceivedPacket, int nextReceivedPacket) {
lossCountSinceLastReport += (nextReceivedPacket - lastReceivedPacket) - 1;
}
static PNVCTL_PACKET_HEADER readNvctlPacket(void) {
NVCTL_PACKET_HEADER staticHeader;
PNVCTL_PACKET_HEADER fullPacket;
@ -83,7 +98,7 @@ static PNVCTL_PACKET_HEADER readNvctlPacket(void) {
return fullPacket;
}
static PNVCTL_PACKET_HEADER sendNoPayloadAndReceive(short ptype, short paylen) {
static int sendMessageAndForget(short ptype, short paylen, const void* payload) {
NVCTL_PACKET_HEADER header;
int err;
@ -91,105 +106,86 @@ static PNVCTL_PACKET_HEADER sendNoPayloadAndReceive(short ptype, short paylen) {
header.payloadLength = paylen;
err = send(ctlSock, (char*) &header, sizeof(header), 0);
if (err != sizeof(header)) {
return 0;
}
if (payload != NULL) {
err = send(ctlSock, payload, paylen, 0);
if (err != paylen) {
return 0;
}
}
return 1;
}
static PNVCTL_PACKET_HEADER sendMessage(short ptype, short paylen, const void* payload) {
int success;
success = sendMessageAndForget(ptype, paylen, payload);
if (!success) {
return NULL;
}
return readNvctlPacket();
}
static void heartbeatThreadFunc(void* context) {
int err;
NVCTL_PACKET_HEADER header;
static void lossStatsThreadFunc(void* context) {
char lossStatsPayload[PPAYLEN_LOSS_STATS];
BYTE_BUFFER byteBuffer;
header.type = PTYPE_HEARTBEAT;
header.payloadLength = PPAYLEN_HEARTBEAT;
while (!PltIsThreadInterrupted(&heartbeatThread)) {
err = send(ctlSock, (char*) &header, sizeof(header), 0);
if (err != sizeof(header)) {
Limelog("Heartbeat thread terminating #1\n");
listenerCallbacks->connectionTerminated(err);
while (!PltIsThreadInterrupted(&lossStatsThread)) {
// Construct the payload
BbInitializeWrappedBuffer(&byteBuffer, lossStatsPayload, 0, PPAYLEN_LOSS_STATS, BYTE_ORDER_LITTLE);
BbPutInt(&byteBuffer, lossCountSinceLastReport);
BbPutInt(&byteBuffer, LOSS_REPORT_INTERVAL_MS);
BbPutInt(&byteBuffer, 1000);
BbPutLong(&byteBuffer, currentFrame);
// Send the message (and don't expect a response)
if (!sendMessageAndForget(PTYPE_LOSS_STATS,
PPAYLEN_LOSS_STATS, lossStatsPayload)) {
Limelog("Loss stats thread terminating #1\n");
return;
}
PltSleepMs(3000);
}
}
// Clear the transient state
lossCountSinceLastReport = 0;
static void jitterThreadFunc(void* context) {
int payload[4];
NVCTL_PACKET_HEADER header;
int err;
header.type = PTYPE_JITTER;
header.payloadLength = PPAYLEN_JITTER;
while (!PltIsThreadInterrupted(&jitterThread)) {
err = send(ctlSock, (char*) &header, sizeof(header), 0);
if (err != sizeof(header)) {
Limelog("Jitter thread terminating #1\n");
listenerCallbacks->connectionTerminated(err);
return;
}
payload[0] = 0;
payload[1] = 77;
payload[2] = 888;
payload[3] = 0; // FIXME: Sequence number?
err = send(ctlSock, (char*) payload, sizeof(payload), 0);
if (err != sizeof(payload)) {
Limelog("Jitter thread terminating #2\n");
listenerCallbacks->connectionTerminated(err);
return;
}
PltSleepMs(100);
// Wait a bit
PltSleepMs(LOSS_REPORT_INTERVAL_MS);
}
}
static void resyncThreadFunc(void* context) {
long long payload[2];
NVCTL_PACKET_HEADER header;
long long payload[3];
PNVCTL_PACKET_HEADER response;
int err;
header.type = PTYPE_RESYNC;
header.payloadLength = PPAYLEN_RESYNC;
while (!PltIsThreadInterrupted(&resyncThread)) {
// Wait for a resync request
PltWaitForEvent(&resyncEvent);
err = send(ctlSock, (char*) &header, sizeof(header), 0);
if (err != sizeof(header)) {
Limelog("Resync thread terminating #1\n");
listenerCallbacks->connectionTerminated(err);
return;
}
// Form the payload
payload[0] = 0;
payload[1] = 0xFFFFF;
payload[2] = 0;
Limelog("Sending resync packet\n");
err = send(ctlSock, (char*) payload, sizeof(payload), 0);
if (err != sizeof(payload)) {
Limelog("Resync thread terminating #2\n");
listenerCallbacks->connectionTerminated(err);
return;
}
// Done capturing the parameters
PltClearEvent(&resyncEvent);
response = readNvctlPacket();
// Send the resync request and read the response
response = sendMessage(PTYPE_RESYNC, PPAYLEN_RESYNC, payload);
if (response == NULL) {
Limelog("Resync thread terminating #3\n");
Limelog("Resync thread terminating #1\n");
listenerCallbacks->connectionTerminated(LastSocketError());
return;
}
Limelog("Resync complete\n");
PltClearEvent(&resyncEvent);
}
}
int stopControlStream(void) {
PltInterruptThread(&heartbeatThread);
PltInterruptThread(&jitterThread);
PltInterruptThread(&lossStatsThread);
PltInterruptThread(&resyncThread);
if (ctlSock != INVALID_SOCKET) {
@ -197,12 +193,10 @@ int stopControlStream(void) {
ctlSock = INVALID_SOCKET;
}
PltJoinThread(&heartbeatThread);
PltJoinThread(&jitterThread);
PltJoinThread(&lossStatsThread);
PltJoinThread(&resyncThread);
PltCloseThread(&heartbeatThread);
PltCloseThread(&jitterThread);
PltCloseThread(&lossStatsThread);
PltCloseThread(&resyncThread);
return 0;
@ -210,8 +204,6 @@ int stopControlStream(void) {
int startControlStream(void) {
int err;
char* config;
int configSize;
PNVCTL_PACKET_HEADER response;
ctlSock = connectTcpSocket(host, 47995);
@ -221,39 +213,21 @@ int startControlStream(void) {
enableNoDelay(ctlSock);
configSize = getConfigDataSize(&streamConfig);
config = allocateConfigDataForStreamConfig(&streamConfig);
if (config == NULL) {
return -1;
}
// Send config
err = send(ctlSock, config, configSize, 0);
free(config);
if (err != configSize) {
return LastSocketError();
}
// Ping pong
response = sendNoPayloadAndReceive(PTYPE_KEEPALIVE, PPAYLEN_KEEPALIVE);
// Send START A
response = sendMessage(PTYPE_START_STREAM_A,
PPAYLEN_START_STREAM_A, PPAYLOAD_START_STREAM_A);
if (response == NULL) {
return LastSocketError();
}
free(response);
// 1405
response = sendNoPayloadAndReceive(PTYPE_1405, PPAYLEN_1405);
// Send START B
response = sendMessage(PTYPE_START_STREAM_B,
PPAYLEN_START_STREAM_B, PPAYLOAD_START_STREAM_B);
if (response == NULL) {
return LastSocketError();
}
free(response);
err = PltCreateThread(heartbeatThreadFunc, NULL, &heartbeatThread);
if (err != 0) {
return err;
}
err = PltCreateThread(jitterThreadFunc, NULL, &jitterThread);
err = PltCreateThread(lossStatsThreadFunc, NULL, &lossStatsThread);
if (err != 0) {
return err;
}

View File

@ -1,97 +0,0 @@
#include "Limelight.h"
#include "PlatformSockets.h"
#include "PlatformThreads.h"
static const char HELLO [] = {
0x07, 0x00, 0x00, 0x00,
0x61, 0x6e, 0x64, 0x72,
0x6f, 0x69, 0x64, 0x03,
0x01, 0x00, 0x00
};
static const char PACKET2 [] = {
0x01, 0x03, 0x02, 0x00,
0x08, 0x00
};
static const char PACKET3 [] = {
0x04, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
static const char PACKET4 [] = {
0x01, 0x01, 0x00, 0x0
};
static SOCKET sock = INVALID_SOCKET;
static int waitAndDiscardResponse(SOCKET sock) {
char temp[256];
return recv(sock, temp, sizeof(temp), 0);
}
void terminateHandshake(void) {
if (sock != INVALID_SOCKET) {
closesocket(sock);
sock = INVALID_SOCKET;
}
}
#include <stdio.h>
int performHandshake(IP_ADDRESS host) {
int err;
sock = connectTcpSocket(host, 47991);
if (sock == INVALID_SOCKET) {
return LastSocketError();
}
enableNoDelay(sock);
err = send(sock, HELLO, sizeof(HELLO), 0);
if (err == SOCKET_ERROR) {
goto CleanupError;
}
err = waitAndDiscardResponse(sock);
if (err == SOCKET_ERROR) {
goto CleanupError;
}
err = send(sock, PACKET2, sizeof(PACKET2), 0);
if (err == SOCKET_ERROR) {
goto CleanupError;
}
err = waitAndDiscardResponse(sock);
if (err == SOCKET_ERROR) {
goto CleanupError;
}
err = send(sock, PACKET3, sizeof(PACKET3), 0);
if (err == SOCKET_ERROR) {
goto CleanupError;
}
err = waitAndDiscardResponse(sock);
if (err == SOCKET_ERROR) {
goto CleanupError;
}
err = send(sock, PACKET4, sizeof(PACKET4), 0);
if (err == SOCKET_ERROR) {
goto CleanupError;
}
closesocket(sock);
sock = INVALID_SOCKET;
return 0;
CleanupError:
closesocket(sock);
sock = INVALID_SOCKET;
return LastSocketError();
}

View File

@ -6,18 +6,14 @@
#include "PlatformThreads.h"
#include "Video.h"
char* allocateConfigDataForStreamConfig(PSTREAM_CONFIGURATION streamConfig);
int getConfigDataSize(PSTREAM_CONFIGURATION streamConfig);
int initializeControlStream(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks);
int startControlStream(void);
int stopControlStream(void);
void destroyControlStream(void);
void connectionSinkTooSlow(int startFrame, int endFrame);
void connectionDetectedFrameLoss(int startFrame, int endFrame);
int performHandshake(IP_ADDRESS host);
void terminateHandshake(void);
void connectionReceivedFrame(int frameIndex);
void connectionLostPackets(int lastReceivedPacket, int nextReceivedPacket);
void initializeVideoDepacketizer(void);
void destroyVideoDepacketizer(void);

View File

@ -9,7 +9,8 @@ typedef struct _NV_VIDEO_PACKET {
int totalPackets;
int flags;
int payloadLength;
char reserved2[36];
int streamPacketIndex;
char reserved2[32];
} NV_VIDEO_PACKET, *PNV_VIDEO_PACKET;
typedef struct _RTP_PACKET {

View File

@ -12,6 +12,7 @@ static int nextPacketNumber;
static int startFrameNumber = 1;
static int waitingForNextSuccessfulFrame;
static int gotNextFrameStart;
static int lastPacketInStream = 0;
static LINKED_BLOCKING_QUEUE decodeUnitQueue;
@ -124,6 +125,9 @@ static void reassembleFrame(int frameNumber) {
// FIXME: Get proper lower bound
connectionSinkTooSlow(0, frameNumber);
}
// Notify the control connection
connectionReceivedFrame(frameNumber);
}
}
}
@ -332,6 +336,13 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
}
}
int streamPacketIndex = videoPacket->streamPacketIndex;
if (streamPacketIndex != (int) (lastPacketInStream + 1)) {
// Packets were lost so report this to the server
connectionLostPackets(lastPacketInStream, streamPacketIndex);
}
lastPacketInStream = streamPacketIndex;
nextPacketNumber++;
// Remove extra padding

View File

@ -129,10 +129,8 @@
<ItemGroup>
<ClCompile Include="AudioStream.c" />
<ClCompile Include="ByteBuffer.c" />
<ClCompile Include="Config.c" />
<ClCompile Include="Connection.c" />
<ClCompile Include="ControlStream.c" />
<ClCompile Include="Handshake.c" />
<ClCompile Include="InputStream.c" />
<ClCompile Include="LinkedBlockingQueue.c" />
<ClCompile Include="PlatformSockets.c" />

View File

@ -18,15 +18,9 @@
<ClCompile Include="ByteBuffer.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Config.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Connection.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Handshake.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ControlStream.c">
<Filter>Source Files</Filter>
</ClCompile>